È possibile farlo con alcuni trucchi. Per prima cosa, definiamo una classe Number
astratta con un parametro generico T
e il divertente parametro generico vincolo where T : Number<T>
. Potrebbe sembrare una ricorsione senza fine, ma in realtà significa solo che T
deve derivare da Number<T>
. Abbiamo bisogno del parametro generico per il trucco implicito di conversione:
public abstract class Number<T>
where T : Number<T>
{
public static Number<T> operator +(Number<T> x, Number<T> y)
{
return x.Sum(y);
}
// Automatic conversion from Number<T> to T
public static implicit operator T(Number<T> x)
{
return (T)x;
}
// Each derived type must supply its own implementation
internal abstract Number<T> Sum(T x);
}
Number
definisce anche un sovraccarico per l'operatore "+". Poiché questo operatore è statico, non è possibile sovrascriverlo nelle classi derivate. Pertanto dichiariamo un metodo Sum
astratto, che l'operatore "+" utilizza per calcolare la sua somma. Implementiamo anche una conversione implicita da Number<T>
a T
.
Ora implementiamo una classe per i numeri interi che deriva da Number<T>
:
public class IntNumber : Number<IntNumber>
{
private int _n;
public IntNumber(int n)
{
_n = n;
}
public int Value { get { return _n; } }
// Automatic conversion from int to IntNumber
public static implicit operator IntNumber(int x)
{
return new IntNumber(x);
}
internal override Number<IntNumber> Sum(IntNumber x)
{
return new IntNumber(_n + x._n);
}
public override string ToString()
{
return _n.ToString();
}
}
La parte importante è l'implementazione del metodo Sum
. La conversione implicita da int
a IntNumber
è facoltativa. Sostituendo il metodo ToString
è più semplice visualizzare IntNumber
. Dichiariamo anche un costruttore e una proprietà Value
che restituisce il numero intero.
Possiamo fare lo stesso con i numeri complessi:
public class ComplexNumber : Number<ComplexNumber>
{
private double _re, _im;
public ComplexNumber(double re, double im)
{
_re = re;
_im = im;
}
public double Re { get { return _re; } }
public double Im { get { return _im; } }
public static ComplexNumber operator +(ComplexNumber x, ComplexNumber y)
{
return x.Sum(y);
}
// Automatic conversion from double to ComplexNumber
public static implicit operator ComplexNumber(double x)
{
return new ComplexNumber(x, 0);
}
// Automatic conversion from IntNumber to ComplexNumber
public static implicit operator ComplexNumber(IntNumber x)
{
return new ComplexNumber(x.Value, 0);
}
internal override Number<ComplexNumber> Sum(ComplexNumber x)
{
return new ComplexNumber(_re + x._re, _im + x._im);
}
public override string ToString()
{
return "(" + _re + "+" + _im + "i)";
}
}
Il sovraccarico dell'operatore nella classe base è necessario per gestire numeri generici digitati (vedi Calcolatrice), quello della classe derivata è richiesto, poiché la conversione automatica da IntNumber
a ComplexNumber
funziona solo con questo .
Ora dichiariamo un calcolatore generico che opera su uno qualsiasi dei nostri tipi di numeri. (È solo una semplice sostituzione di NumericTimeSeries a scopo di test).
public class Calculator<T>
where T : Number<T>
{
public T GetSum(T x, T y)
{
return x + y; // <== You can add any numbers with generic type T with "+"
}
}
Si noti che possiamo semplicemente aggiungere due numeri di un tipo generico con l'operatore "+". A causa dell'operatore implicito dichiarato in Number<T>
non abbiamo bisogno di alcun cast!
Cerchiamo di testare l'aggiunta di tipi di numeri generici con il calcolatore generico e l'aggiunta diretta di diversi tipi di numeri:
IntNumber i1 = new IntNumber(2);
IntNumber i2 = new IntNumber(3);
var intCalculator = new Calculator<IntNumber>();
Console.WriteLine(intCalculator.GetSum(i1, i2)); // ==> 5
Console.WriteLine(intCalculator.GetSum(4, 6)); // ==> 10
ComplexNumber c1 = new ComplexNumber(2, 7);
ComplexNumber c2 = new ComplexNumber(3, -1);
var complexCalculator = new Calculator<ComplexNumber>();
Console.WriteLine(complexCalculator.GetSum(c1, c2)); // ==> (5+6i)
Console.WriteLine(c1 + 100.5); // ==> (102.5+7i)
Console.WriteLine(c1 + i1); // ==> (4+7i)
Console.WriteLine(i1 + c1); // ==> (4+7i)