1

我知道有很多这样的问题
"Operator 'whatever' cannot be applied to operands of type 'T'(可能是 ++、+= 或 <= 等等等。但请耐心等待,我想问一些不同的问题。

假设我有这个代码

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;
    }
}

即使我已经制作了我的类型结构和值类型,我也得到了错误 Operator '+=' cannot be applied to operands of type 'T' and 'T'

如果我尝试变得微妙并应用 ValueType 约束,它会说 Constraint cannot be special class 'System.ValueType'

如果我尝试将 for 循环中的参数转换为类型 T,然后执行此操作..

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;
    }
}

我仍然收到错误

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'

无论如何,我无法让它工作。我正在使用 VS2010(C# 4.0、.NET 4.0)。所以我想知道 C# 5.0 何时最终与 VS2012 一起发布(据我所知它们仍处于 beta 阶段,对吗?)它会解决这些问题吗?还是我们再次对泛型的使用有如此多的限制?

4

3 回答 3

2

没有语言改进是可能的,但是您必须使用一些技巧并且不能将其应用于现有的数字类型,而必须创建新的数字类型。其中之一是奇怪地反复出现的模式class C<T> : where T : C<T>。另一个技巧是使用静态委托进行操作。我定义了一个这样的数字类(为了简单起见,我只定义了加法):

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);
    }
}

请注意,我们必须在重载运算符时指定至少一种类型Numeric<T>(一种类型必须始终是重载运算符的类)。另请注意,由于通用约束,我们可以强制Numeric<T>转换为Twhere T : Numeric<T>

现在我们可以像这样声明一个计算器。由于Numeric<T>重载了+运算符,我们可以+=在这里使用。

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;
    }
}

现在让我们定义一个具体的 Numeric 类。在静态构造函数中,我们定义了静态Add委托。由于操作符是静态的,所以这个委托也必须是静态的,因为它是从操作符方法中调用的,而且由于静态成员不能是虚拟的,所以我们必须使用这个委托技巧。

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);
    }
}

现在让我们测试这个棘手的结构

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)

    }
}
于 2012-07-28T14:34:06.747 回答
1

不,它不会改善这个问题。C#5 将提供 async-await 和一些小功能。但不是与方法/运算符重载一起使用的泛型的扩展版本。

对于比较,您可以使用IComparer<T>andIComparable<T>作为解决方法,但对于算术,没有好的解决方案。有一些技术,但它们要么使 API 变得丑陋,要么很慢。


如果我试图变得微妙并应用 ValueType 约束,它会说“约束不能是特殊类System.ValueType

此约束的等效项是struct关键字 ie where T: struct。但是限制到值类型并不会在这里为您带来任何好处。为什么会这样?有不支持算术的值类型,有支持的引用类型。因此,作为一个值类型与您的需求是正交的。

于 2012-07-28T13:11:57.840 回答
0

不幸的是,当前处于 RC 状态的 C# 5.0 没有任何变化。它主要关注异步编程。

于 2012-07-28T13:12:14.447 回答