演算にジェネリックが含まれる場合の対処方法
MSDNフォーラムの以下の質問について考えてみた。
Generics で四則演算がしたい。
http://forums.microsoft.com/MSDN-JA/ShowPost.aspx?PostID=522960&SiteID=7
さて、ここからが考察です。まず、下記のコードを実行すると、次のようなエラーが出る。
class testOperator<T>
{
public static T Add(T a, T b)
{
return a + b;
}
}
エラー 1 演算子 '+' を 'T' と 'T' 型のオペランドに適用することはできません。
Tはoperator +を実装しているとは限らないので、言われてみれば当たり前のことである。しかし、プログラマがintやdoubleなどを想定していた場合、あまりに無情である。なんで~!どうしてできないの~!っていうことになる。そこで、Tにはoperator +を実装されたものしか使いませんよという制約を付けることによって許してもらえないだろうか?例えば、
class testOperator<T>
where T:IOperator
のようにである。IOperatorはここで勝手に付けた名前であり、一連のOperator演算子を実装しているものとします。
というわけで
C# 3.0辺りでなんとかなりませんか?
ちなみに上の代替コードの例は以下のようになります。
interface ICalculator<T>
{
T Add(T a, T b);
}
struct IntCalculator : ICalculator<int>
{
public int Add(int a, int b) { return a + b; }
}
class testOperator<T, C> where C : ICalculator<T>, new()
{
public static T Add(T a, T b)
{
C calculator = new C();
return calculator.Add(a, b);
}
}
なお、calculator.Add(a, b)ではなく、本当にa + bを返したい場合は、以下のように少し複雑になる。ポイントとしては、Tにoperator +が実装されていることをコンパイラにわからせるしかない。したがって、Tをstructでラップし、そのstructでoperator +を定義することになる。
interface ICalculator<T>
{
T Add(T a, T b);
}
struct IntCalculator : ICalculator<int>
{
public int Add(int a, int b) { return a + b; }
}
struct Number<T, C> where C : ICalculator<T>, new()
{
private T _Value;
private static C calculator = new C();
private Number(T Value)
{
this._Value = Value;
}
public T Value { get { return _Value; } }
public static implicit operator Number<T, C>(T a)
{
return new Number<T, C>(a);
}
public static implicit operator T(Number<T, C> a)
{
return a._Value;
}
public static Number<T, C> operator +(Number<T, C> a, Number<T, C> b)
{
return calculator.Add(a.Value, b.Value);
}
}
class testOperator<T, C> where C : ICalculator<T>, new()
{
public static T Add(T a, T b)
{
C calculator = new C();
return (Number<T, C>)a + (Number<T, C>)b;
}
}
(参考)
Using generics for calculations
http://www.codeproject.com/csharp/genericnumerics.asp