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

爱⌒轻易说出口 提交于 2019-12-13 19:19:36

问题


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?


回答1:


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)

    }
}



回答2:


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 i.e. 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.




回答3:


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



来源:https://stackoverflow.com/questions/11701304/has-generic-types-features-operator-overloading-improved-in-c-sharp-5-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!