简体   繁体   中英

Has generic type's features/operator overloading improved in C# 5.0?

I know there are many question on SO like this
"Operator 'whatever' cannot be applied to operands of type 'T' (whatever may be ++, += or <= etc. etc. But bear with me, I wanna ask something different.

Let's say I have this code

public class GenericCls<T> where T : struct
{
    public static T AddValues(params T[] values)
    {
        T sum = default(T);
        if (values != null)
        {
            for (int i = 0; i < values.Length; i++)
            {
                sum += values[i];
            }
        }
        return sum;
    }
}

even though I've made my type struct, and value type I get the error Operator '+=' cannot be applied to operands of type 'T' and 'T'

If I try to be subtle and apply the ValueType constraint, it says Constraint cannot be special class 'System.ValueType'

If I try to turn the parameter in for loop to type T, and do this..

public class GenericCls<T> where T : struct, IComparable<T>, IEquatable<T>
{
    public static T AddValues(params T[] values)
    {
        T sum = default(T);
        if (values != null)
        {
            for (T i = default(T); i < values.Length; i++)
            {
                sum += values[i];
            }
        }
        return sum;
    }
}

I still get errors

Operator '<' cannot be applied to operands of type 'T' and 'int'
Operator '++' cannot be applied to operand of type 'T'
Cannot implicitly convert type 'T' to 'int'

No matter what, I can't get it to work. I am using VS2010 (C# 4.0, .NET 4.0). So I wanna know when C# 5.0 would be finally released with VS2012 (as far as I know they are still in beta stage right?) would it take care of these issues? Or are we again left with so many restriction on usage of Generics?

It is possible without language improvements, but you have to use some tricks and cannot apply it to existing numeric types, but have to create new ones. One of them is the curiously recurring pattern class C<T> : where T : C<T> . Another trick is to use static delegates for the operations. I define a numeric class like this (for the sake of simplicity I only define the addition):

public abstract class Numeric<T>
    where T : Numeric<T>
{
    public static Func<T, T, T> Add;

    public static T operator +(Numeric<T> x, Numeric<T> y)
    {
        if (x == null) {
            return (T)y;
        }
        if (y == null) {
            return (T)x;
        }
        return Add((T)x, (T)y);
    }
}

Note that we have to specify at least one type as Numeric<T> when overloading the operator (one type must always be the class that overloads the operator). Note also that we can cast Numeric<T> to T because of the generic constraint where T : Numeric<T>

Now we can declare a calculator like this. Since Numeric<T> overloads the + operator, we can use += here.

public class Calculator<T> where T : Numeric<T>
{
    public static T AddValues(params T[] values)
    {
        T sum = default(T);
        if (values != null) {
            for (int i = 0; i < values.Length; i++) {
                sum += values[i];
            }
        }
        return sum;
    }
}

Now lets define a concrete Numeric class. In the static constructor we define the static Add delegate. Since the operators are static, this delegate must be static as well because it is called from within the operator method, and since static members cannot be virtual, we must use this delegate-trick.

public class Complex : Numeric<Complex>
{
    static Complex()
    {
        Add = (x, y) => new Complex(x.Re + y.Re, x.Im + y.Im);
    }

    public double Re { get; private set; }
    public double Im { get; private set; }

    public Complex(double re, double im)
    {
        Re = re;
        Im = im;
    }

    public override string ToString()
    {
        return String.Format("({0}, {1})", Re, Im);
    }
}

Now lets test this tricky construction

static class Test
{
    public static void AddComplexNumbers()
    {
        // Using the calculator
        var numbers = new Complex[] { new Complex(2, 7), new Complex(6, -2) };
        var result = Calculator<Complex>.AddValues(numbers);
        Console.WriteLine(result); // ==> (8, 5)

        // Directly
        var c1 = new Complex(2, 7);
        var c2 = new Complex(6, -2);
        result = c1 + c2;
        Console.WriteLine(result); // ==> (8, 5)

    }
}

No, it will not improve this issue. C#5 will offer async-await and a few minor features. But not an extended version of generics that works with method/operator overloading.

For comparisons you can use IComparer<T> and IComparable<T> as a workaround, but for arithmetic there is no good solution. There are a few techniques, but they either make the API ugly, or they're slow.


If I try to be subtle and apply the ValueType constraint, it says "Constraint cannot be special class System.ValueType "

The equivalent of this constraint is the struct keyword ie where T: struct . But constraining to value types does not gain you anything here. And why would it? There are value types that don't support arithmetic, and there are reference types that do. So being a value-type is orthogonal to what you need.

Unfortunately nothing changes with C# 5.0, which is currently in RC state. It focused mainly on async programming.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM