Generici – dove T è un numero?

Sto cercando di capire un modo per creare una class generica solo per i tipi di numeri, per fare alcuni calcoli.

C’è un’interfaccia comune per tutti i tipi di numeri (int, double, float …) che mi manca ???

In caso contrario, quale sarà il modo migliore per creare una class del genere?

AGGIORNARE:

La cosa principale che sto cercando di ottenere è verificare chi è il più grande tra due variabili di tipo T.

Quale versione di .NET stai usando? Se si utilizza .NET 3.5, quindi ho un’implementazione di operatori generici in MiscUtil (gratuito, ecc.).

Questo ha metodi come T Add(T x, T y) e altre varianti per l’aritmetica su diversi tipi (come DateTime + TimeSpan ).

Inoltre, questo funziona per tutti gli operatori integrati, sollevati e personalizzati e memorizza nella cache il delegato per le prestazioni.

Qualche altro background sul perché questo è difficile è qui .

Potresti anche voler sapere che l’ordinamento dynamic (4.0) risolve indirettamente anche questo problema – cioè

 dynamic x = ..., y = ... dynamic result = x + y; // does what you expect 

Per il commento su < / > - non hai effettivamente bisogno di operatori per questo; hai solo bisogno di:

 T x = ..., T y = ... int c = Comparer.Default.Compare(x,y); if(c < 0) { // x < y } else if (c > 0) { // x > y } 

Ci sono interfacce per alcune operazioni sui tipi di numeri, come IComparable , IConvertible e IEquatable . Puoi specificarlo per ottenere una funzionalità specifica:

 public class MaxFinder where T : IComparable { public T FindMax(IEnumerable items) { T result = default(T); bool first = true; foreach (T item in items) { if (first) { result = item; first = false; } else { if (item.CompareTo(result) > 0) { result = item; } } } return result; } } 

È ansible utilizzare i delegati per espandere una class con operazioni specifiche del tipo:

 public class Adder { public delegate T AddDelegate(T item1, T item2); public T AddAll(IEnumerable items, AddDelegate add) { T result = default(T); foreach (T item in items) { result = add(result, item); } return result; } } 

Uso:

 Adder adder = new Adder(); int[] list = { 1, 2, 3 }; int sum = adder.AddAll(list, delegate(int x, int y) { return x + y; }); 

È inoltre ansible memorizzare i delegati nella class e disporre di metodi di fabbrica diversi che configurano i delegati per un tipo di dati specifico. In questo modo il codice specifico del tipo è solo nei metodi di fabbrica.

Non puoi farlo, dal momento che dovresti usare una singola interfaccia per operazioni aritmetiche. Ci sono state molte richieste su Connect per aggiungere un’interfaccia IArithmetic per questo scopo specifico, ma finora sono state tutte respinte.

È ansible risolvere il problema definendo una struttura senza membri, che implementa un’interfaccia “Calcolatrice”. Abbiamo adottato questo approccio in una class generica di interpolazione nel Plutone Toolkit . Per un esempio dettagliato, qui abbiamo un’implementazione del calcolatore “vettoriale”, che consente al nostro generico interpolatore di funzionare con i vettori. Ce ne sono di simili per galleggianti, doppi, quaternioni, ecc.

Il più vicino che ottieni è struct che ho paura. Dovrai fare controlli più estesi per i tipi di numeri nel codice.

 public class MyClass where T : struct (...) 

Nel Framework BCL (libreria di classi di base), molte funzioni numeriche (come le funzioni in System.Math) trattano di ciò avendo sovraccarichi per ogni tipo numerico.

La class Math statica nel BCL contiene metodi statici, che è ansible chiamare senza dover creare un’istanza della class. Potresti fare lo stesso nella tua class. Ad esempio, Math.Max ​​ha 11 overload:

 public static byte Max(byte val1, byte val2); public static decimal Max(decimal val1, decimal val2); public static double Max(double val1, double val2); public static short Max(short val1, short val2); public static int Max(int val1, int val2); public static long Max(long val1, long val2); public static sbyte Max(sbyte val1, sbyte val2); public static float Max(float val1, float val2); public static ushort Max(ushort val1, ushort val2); public static uint Max(uint val1, uint val2); public static ulong Max(ulong val1, ulong val2); 
 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace GenericPratice1 { public delegate T Del(T numone, T numtwo)where T:struct; class Class1 { public T Addition(T numone, T numtwo) where T:struct { return ((dynamic)numone + (dynamic)numtwo); } public T Substraction(T numone, T numtwo) where T : struct { return ((dynamic)numone - (dynamic)numtwo); } public T Division(T numone, T numtwo) where T : struct { return ((dynamic)numone / (dynamic)numtwo); } public T Multiplication(T numone, T numtwo) where T : struct { return ((dynamic)numone * (dynamic)numtwo); } public Del GetMethodInt(int ch) where T:struct { Console.WriteLine("Enter the NumberOne::"); T numone =(T) Convert.ChangeType((object)(Console.ReadLine()), typeof(T)); Console.WriteLine("Enter the NumberTwo::"); T numtwo = (T)Convert.ChangeType((object)(Console.ReadLine()), typeof(T)); T result = default(T); Class1 c = this; Del deleg = null; switch (ch) { case 1: deleg = c.Addition; result = deleg.Invoke(numone, numtwo); break; case 2: deleg = c.Substraction; result = deleg.Invoke(numone, numtwo); break; case 3: deleg = c.Division; result = deleg.Invoke(numone, numtwo); break; case 4: deleg = c.Multiplication; result = deleg.Invoke(numone, numtwo); break; default: Console.WriteLine("Invalid entry"); break; } Console.WriteLine("Result is:: " + result); return deleg; } } class Calculator { public static void Main(string[] args) { Class1 cs = new Class1(); Console.WriteLine("Enter the DataType choice:"); Console.WriteLine("1 : Int\n2 : Float"); int sel = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("Enter the choice::"); Console.WriteLine("1 : Addition\n2 : Substraction\3 : Division\4 : Multiplication"); int ch = Convert.ToInt32(Console.ReadLine()); if (sel == 1) { cs.GetMethodInt(ch); } else { cs.GetMethodInt(ch); } } } } 

Non credo che tu possa definirlo usando un vincolo di tipo generico. Il tuo codice potrebbe verificare internamente i tuoi requisiti, possibilmente utilizzando Double.Parse o Double.TryParse per determinare se è un numero– o se VB.NET non è fuori questione, allora potresti usare la funzione IsNumeric ().

Modifica: è ansible aggiungere un riferimento a Microsoft.VisualBasic.dll e chiamare la funzione IsNumeric () da c #

Non puoi farlo solo in fase di compilazione. Ma potresti mettere più vincoli per eliminare la maggior parte dei ‘tipi cattivi’ sul tuo tipo numerico come sotto

class yourclass dove T: IComparable, IFormattable, IConvertible, IComparabe , IEquatable , struct {… Alla fine si dovrebbe comunque verificare in runtime se il proprio tipo è accettabile usando object.GetType ( ) metodo.

Se solo si confronta, IComparable da solo fa il trucco.