С# Добавление двух общих значений
Может кто-нибудь объяснить, почему это не сработает? Я пытался добавить два значения независимо от числового типа.
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.
Он не знает, даже это класс или нет.