C#添加两个通用值

时间:2011-11-14 13:54:54

标签: generics c#-4.0

有人可以解释为什么这不起作用?无论数字类型如何,我都试图能够添加两个值。

public static T Add<T> (T number1, T number2)
{
    return number1 + number2;
}

编译时,我收到以下错误:

Operator '+' cannot be applied to operands of type 'T' and 'T'

12 个答案:

答案 0 :(得分:57)

没有通用约束允许您强制执行运算符重载。您可以查看following library。或者,如果您使用的是.NET 4.0,则可以使用dynamic关键字:

public static T Add<T>(T number1, T number2)
{
    dynamic a = number1;
    dynamic b = number2;
    return a + b;
}

显然,这并不适用于任何编译时安全性,这是泛型的意思。应用编译时安全性的唯一方法是强制执行通用约束。对于您的场景,没有可用的约束。这只是欺骗编译器的技巧。如果Add方法的调用者没有传递与+运算符一起使用的类型,则代码将在运行时抛出异常。

答案 1 :(得分:24)

这里给出的解决方案运行良好,但我想我会添加另一个使用表达式

的解决方案
public static T Add<T>(T a, T b)
{
    // Declare the parameters
    var paramA = Expression.Parameter(typeof(T), "a");
    var paramB = Expression.Parameter(typeof(T), "b");

    // Add the parameters together
    BinaryExpression body = Expression.Add(paramA, paramB);

    // Compile it
    Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();

    // Call it
    return add(a, b);
}

这样您就可以创建执行添加的Func<T, T, T>。 完整的解释可以在this article

中找到

答案 2 :(得分:9)

编译器不知道类型T,因此无法找到在任何地方定义的重载+运算符...

您目前可以做的最好的事情就是声明您的方法(因为所有数字类型都可以转换为double):

public static double Add (double number1, double number2)
{
  return number1 + number2;
}

或者如果您确定将定义合适的+运算符:

public static T Add<T>(T number1, T number2)
{
  dynamic dynamic1 = number1;
  dynamic dynamic2 = number2;
  return dynamic1 + dynamic2;
}

<强>更新

或两者的组合:

public static T Add<T>(T in1, T in2)
{
    var d1 = Convert.ToDouble(in1);
    var d2 = Convert.ToDouble(in2);
    return (T)(dynamic)(d1 + d2);
}

答案 3 :(得分:5)

当我想要一个可以在任意数字列表上工作的泛型方法时,我就遇到了这个问题,例如:

public T Calculate<T>(IEnumerable<T> numbers);

我找到的解决方案是使用lambda表达式:

public T Calculate<T>(Func<T, T, T> add, IEnumerable<T> numbers);

然后通过

调用
var sum = this.Calculate((a, b) => a + b, someListOfNumbers);

显然在某些情况下,您可能只是在代码中使用+而不是lambda表达式,但如果您需要以通用方式执行数学运算,这是我可以提出的最简单的方法这是编译安全的。

答案 4 :(得分:4)

Since this is generic type without a constraint compiler has no knowledge if the types involved will have '+' overloaded hence the compiler error

These are some workarounds

public static TResult Add<T1, T2, TResult>(T1 left, T2 right, Func<T1, T2, TResult> AddMethod)
{
    return AddMethod(left, right);
}

var finalLabel = Add("something", 3,(a,b) => a + b.ToString());


Below code could let you build same but evaluated at run time so not run time safe

public static T AddExpression<T>(T left, T right)
{
    ParameterExpression leftOperand = Expression.Parameter(typeof(T), "left");
    ParameterExpression rightOperand = Expression.Parameter(typeof(T), "right");
    BinaryExpression body = Expression.Add(leftOperand, rightOperand);
    Expression<Func<T, T, T>> adder = Expression.Lambda<Func<T, T, T>>(
        body, leftOperand, rightOperand);
    Func<T, T, T> theDelegate = adder.Compile();
    return theDelegate(left, right);
}

答案 5 :(得分:2)

尝试此代码。这是添加的通用代码。

public static dynamic AddAllType(dynamic x, dynamic y)
{
    return  x + y;
}

答案 6 :(得分:1)

        public class generic<T>
        {
            T result;
            public generic(T eval1, T eval2)
            {

                dynamic a = eval1;
                dynamic b = eval2;
                result = a + b;
                Console.WriteLine(result);
                Console.ReadLine();


            }

             static void Main(string[] args)
        {

             generic<int> genobj = new generic<int>(20,30);

        }

答案 7 :(得分:1)

这可以防止开发人员编写错误的代码。几个问题不能更好地解释这个问题:

1&GT;编译器是否知道类型[否,因为它是通用类型]。 2 - ;它可以接受对象作为参数[是的,它可以并且因此它不知道如何处理它们 - &gt;越野车代码]

因为,您希望防止您的代码出错,C#不允许您使用&#39; +&#39; &#39; - &#39;和其他运营商。

解决方案: 你可以使用&#34; dynamic&#34;,&#34; var&#34;如果需要,可以输入并返回它们的总和。

答案 8 :(得分:0)

如果您不想(或不能)在运行时使用动态类型或生成代码,那么我可以从EqualityComparer实现中借用另一种方法。这有点冗长,但是使用“经典” .NET工具的另一种可能性。

首先,您定义一个类似于“特征”的通用接口,定义您想要的添加方法:

/// <summary>
/// Represents an interface defining the addition calculation for generic types.
/// </summary>
public interface IAddition<T>
{
    /// <summary>
    /// Calculates the addition result of <paramref name="left"/> and <paramref name="right"/>.
    /// </summary>
    /// <param name="left">The left operand.</param>
    /// <param name="right">The right operand.</param>
    /// <returns>The calculation result.</returns>
    T Add(T left, T right);
}

现在,您针对感兴趣的类型实现此特征,在此示例中,我针对float进行了此操作:

internal class SingleAddition : IAddition<Single>
{
    Single IAddition<Single>.Add(Single left, Single right) => left + right;
}

然后创建一个通用的静态类,它将是您将实际与之交互的类。它会自动创建并缓存适合其通用类型的IAddition<T>接口的实现:

/// <summary>
/// Represents an implementation of addition calculations for generic types.
/// </summary>
/// <typeparam name="T">The type to support the addition calculation.</typeparam>
public static class Addition<T>
{
    private static readonly IAddition<T> _implementation = Implement();

    /// <summary>
    /// Calculates the addition result of <paramref name="left"/> and <paramref name="right"/>.
    /// </summary>
    /// <param name="left">The left operand.</param>
    /// <param name="right">The right operand.</param>
    /// <returns>The calculation result.</returns>
    public static T Add(T left, T right) => _implementation.Add(left, right);

    private static IAddition<T> Implement()
    {
        Type type = typeof(T);
        // If T implements IAddition<T> return a GenericAddition<T>.
        if (typeof(IAddition<T>).IsAssignableFrom(type))
            return (IAddition<T>)Activator.CreateInstance(typeof(GenericAddition<>).MakeGenericType(type));
        // Otherwise use a default implementation for primitive types.
        switch (type.IsEnum ? Type.GetTypeCode(Enum.GetUnderlyingType(type)) : Type.GetTypeCode(type))
        {
            case TypeCode.Single:
                return (IAddition<T>)new SingleAddition();
            default:
                throw new NotSupportedException($"Type {type.Name} does not support addition.");
        }
    }
}

internal sealed class GenericAddition<T> : IAddition<T> where T : IAddition<T>
{
    T IAddition<T>.Add(T left, T right) => left.Add(left, right);
}

当然,您可以扩展类型代码的开关以支持您感兴趣的所有类型。这些都是原始类型,并且您不能修改,因为您可以简单地实现IAddition<T>接口您可以可以修改的类型-GenericAddition实现将随后使用它。

请注意,由于检查了我添加的基础类型,因此已经支持Enum值。

正如您在静态Addition<T>类中所看到的,它公开了一个Add方法,您最终将像这样从外部调用该方法:

public class SomeStuff<T>
{
    public T TestAddition(T left, T right) => Addition<T>.Add(left, right);
}

// Testing in C# interactive:
> SomeStuff<float> floatStuff = new SomeStuff<float>();
> floatStuff.TestAddition(12, 24.34f)
36.34

学校数学从未如此简单!也许。并不是的。它是机密的。

答案 9 :(得分:0)

如果您可以将泛型类型 T 包装在某种“容器”中,则可以使用以下方法。我为 Totalint 提供了一种方法 (double),但您可以轻松添加其他类型和方法。

顺便说一下,这类似于 Linq 如何为 Sum 定义扩展方法(例如 IEnumerable<T>)。

internal struct GenericContainer<T> {
    internal T A { get; set; }
    internal T B { get; set; }
}

/// <summary>
/// Extension methods for GenericContainer<T>
/// </summary>
internal static class GenericContainerExtensions {
    internal static int Total(this GenericContainer<int> gc) {
        return gc.A + gc.B;
    }
    internal static double Total(this GenericContainer<double> gc) {
        return gc.A + gc.B;
    }
}

class Program {
    static void Main(string[] args) {
        var gc = new GenericContainer<double>() { A = 3.3, B = 4.4 };
        Console.WriteLine($"gc.Total() = {gc.Total()}");
    }
}

答案 10 :(得分:-1)

因为没有人通过使用对象提供解决方案来回答。因此我正在添加我的解决方案。在此,您必须将通用值转换为object。

所以这是工作代码:

public static T Add<T>(T number1, T number2)
{
    object a = number1;
    object b = number2;
    return (T)(object)((int)a * (int)b).ToString();
}

答案 11 :(得分:-2)

正如您从错误消息中看到的那样,运算符'+'无法应用。 发生这种情况是因为编译器对T类型一无所知。 它甚至都不知道这是不是一个班级。