С# Добавление двух общих значений

Может кто-нибудь объяснить, почему это не сработает? Я пытался добавить два значения независимо от числового типа.

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

Когда я скомпилирую это, я получаю следующую ошибку:

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

Ответ 1

Нет общего ограничения, которое позволяет принудительно выполнять перегрузку оператора. Вы можете посмотреть следующую библиотеку. Альтернативно, если вы используете .NET 4.0, вы можете использовать ключевое слово dynamic:

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

Очевидно, что это не относится к какой-либо безопасности времени компиляции, к которой предназначены дженерики. Единственный способ применения безопасности во время компиляции - обеспечить соблюдение общих ограничений. И для вашего сценария нет ограничений. Это всего лишь трюк, чтобы обмануть компилятор. Если вызывающий объект метода Add не пропускает типы, которые работают с оператором +, код будет генерировать исключение во время выполнения.

Ответ 2

Решения, приведенные здесь, работают хорошо, но я думал, что добавлю еще один, который использует выражения

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>, который выполняет добавление. Полное объяснение можно найти в в этой статье.

Ответ 3

Тип 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);
}

Ответ 4

Это проблема, с которой я столкнулся, когда мне нужен общий метод, который мог бы работать с произвольными списками чисел, например:

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

Решение, которое я нашел, заключалось в использовании выражения лямбда:

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

Что вы тогда вызываете

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

Очевидно, что в некоторых случаях у вас может быть только + внутри кода вместо лямбда-выражений, но если вам нужно выполнять математические операции в общем виде, это было самое легкое, что я мог придумать, это скомпилировано.

Ответ 5

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

Ответ 6

Попробуйте этот код. Это общий код для добавления.

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

Ответ 7

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

        }

Ответ 8

Это не позволяет разработчикам писать багги-код. Несколько вопросов, чтобы лучше объяснить вопрос:

1 > Знает ли компилятор тип [Нет, потому что это общий тип]. 2 > Может ли он принимать объекты как параметр [Да, он может и из-за этого он не будет знать, что с ними делать → багги-код]

Так как вы хотите, чтобы ваш код не был ошибкой, С# не позволяет вам иметь "+" и "другие".

Решение: вы можете использовать тип "dynamic", "var" и можете возвращать их суммирование, если это необходимо.

Ответ 9

Потому что никто не ответил, предлагая решение с использованием объекта. Поэтому я добавляю свое решение. В этом случае вы должны указать свои общие значения для объекта.

Итак, это рабочий код:

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

Ответ 10

Если вы не хотите (или не можете) использовать динамические типы или генерировать код во время выполнения, есть другой подход, который я позаимствовал из реализации 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

Школьная математика еще никогда не была такой простой! Может быть. На самом деле, нет. Это классифицировано.

Ответ 11

Как видно из сообщения об ошибке, оператор "+" не может быть применен. Это происходит потому, что компилятор ничего не знает о типе T. Он не знает, даже это класс или нет.