2011-11-14 11 views
20

Qualcuno può spiegare perché questo non funzionerà? Stavo cercando di essere in grado di aggiungere due valori indipendentemente dal tipo numerico.C# Aggiunta di due valori generici

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

Quando compilo questo, ottengo il seguente errore:

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

Il compilatore C# esegue questa operazione in modo da non scrivere codice buggato. Se T è sconosciuto al compilatore non sa come aggiungere i valori di T. Inoltre non conosce il tipo di risultato di T, in questo caso l'aggiunta. – JonH

+0

Gli argomenti sono forzati ad essere "di tipo numerico" ?? –

risposta

40

Non esiste un vincolo generico che consente di imporre il sovraccarico dell'operatore. Puoi dare un'occhiata allo following library. In alternativa, se si utilizza .NET 4.0 è possibile utilizzare la parola chiave dynamic:

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

Ovviamente questo non si applica alcuna sicurezza fase di compilazione che è quello che i generici sono destinati. L'unico modo per applicare la sicurezza in fase di compilazione è applicare i vincoli generici. E per il tuo scenario non c'è nessun vincolo disponibile. È solo un trucco per imbrogliare il compilatore. Se il chiamante del metodo Add non passa i tipi che funzionano con l'operatore +, il codice genererà un'eccezione in fase di runtime.

+1

+1 per la spiegazione e il risultato del passaggio di dati non validi. – JonH

+0

Penso che ora ci sia un vincolo .. Dettagli [qui] (https://msdn.microsoft.com/en-us/library/bb384067.aspx) – Aamir

+0

@Aamir Il vincolo "dove" è sempre stato lì. Il punto è che non c'è modo di specificare "dove T può essere aggiunto ad altri Ts" in quella clausola :) – Frans

-2

Come si può vedere dal messaggio di errore operatore '+' non può essere applicato. Ciò si verifica perché il compilatore non sa nulla del tipo T. Non sa nemmeno se si tratta di una classe o meno.

+2

Non è una buona risposta. – JonH

+0

@JonH mmm .. potrebbe essere che hai ragione. Non so come spiegare in poche parole che i generici C# sono molto limitati in confronto ai modelli C++ – VMykyt

+1

Questo non ha nulla a che fare con le limitazioni. Se fatto correttamente praticamente tutto è possibile. La tua risposta originale era 'come vedi dall'operatore del messaggio di errore + non può essere applicato '. Questa non è una buona risposta, non ho fatto un downvote, ma non è così. – JonH

8

tipo T non è conosciuto dal compilatore, in modo che non riesce a trovare un sovraccarico + operatore definito da nessuna parte ...

Il meglio attualmente si può fare è dichiarare il vostro metodo in quanto tale (in quanto tutti i tipi numerici sono convertibili ad doppia):

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

o se sei sicuro che saranno definiti un apposito operatore +:

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

Aggiornato

o una combinazione dei due:

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

I decimali non possono essere convertiti in doppio implicitamente. – kol

+2

@kol Buon punto, rimosso implicitamente dalla mia risposta. –

3

Questo è un problema che ho appena avuto quando ho voluto un metodo generico che potrebbe funzionare su liste arbitrarie di numeri, ad esempio:

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

la soluzione che ho trovato è stato quello di usare un'espressione lambda:

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

che hai il n chiamata

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

Ovviamente in alcuni casi si può anche semplicemente avere + all'interno del codice, invece di espressioni lambda, ma se si necessità per eseguire operazioni matematiche in modo generico questa è stata la cosa più semplice che potevo venire con quello è compilare sicuro.

15

Le soluzioni qui riportati funzionano bene, ma ho pensato che vorrei aggiungere un altro che usa espressioni

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

In questo modo si sta creando un Func<T, T, T> che esegue l'aggiunta. La spiegazione completa è disponibile in this article.

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

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

Ciò impedisce agli sviluppatori di scrivere codice buggy. Poche domande per spiegare meglio la domanda:

1> Il compilatore conosce il tipo [No, perché è un tipo generico]. 2> Può accettare oggetti come parametro [Sì, può e per questo non saprà cosa fare con loro -> codice buggy]

Poiché, si desidera impedire al codice di essere bacato, C# non ti permette di avere '+' '-' e altri operatori.

Soluzione: è possibile utilizzare il tipo "dinamico", "var" e può restituire la sommatoria di essi, se necessario.

1

Perché nessuno ha risposto fornendo una soluzione utilizzando l'oggetto. Pertanto sto aggiungendo la mia soluzione. In questo devi trasmettere i tuoi valori generici all'oggetto.

Quindi questo è il codice di lavoro:

1

provare questo codice. Questo è un codice generico per l'aggiunta.

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