タイプごとに乗算メソッドを明示的に記述する必要があります。
ただし、このコンパイル可能なコード サンプルが示すように、少し単純化できます。
using System;
namespace Demo
{
internal class Program
{
static void Main()
{
var d = new Vector4D<double>{v1=1, v2=2, v3=3, v4=4};
Console.WriteLine(d*2); // Prints 2, 4, 6, 8
var i = new Vector4D<int>{v1=1, v2=2, v3=3, v4=4};
Console.WriteLine(i*2); // Prints 2, 4, 6, 8
// This will throw a "NotSupported" exception:
var s = new Vector4D<string>{v1="1", v2="2", v3="3", v4="4"};
Console.WriteLine(s*"");
}
}
partial struct Vector4D<T>
{
public T v1;
public T v2;
public T v3;
public T v4;
public static Vector4D<T> operator *(Vector4D<T> a, T b)
{
a.v1 = multiply(a.v1, b);
a.v2 = multiply(a.v2, b);
a.v3 = multiply(a.v3, b);
a.v4 = multiply(a.v4, b);
return a;
}
public override string ToString()
{
return string.Format("v1: {0}, v2: {1}, v3: {2}, v4: {3}", v1, v2, v3, v4);
}
private static Func<T, T, T> multiply;
}
// Partial just to keep this logic separate.
partial struct Vector4D<T>
{
static Vector4D() // Called only once for each T.
{
if (typeof(T) == typeof(int))
Vector4D<int>.multiply = (a, b) => a*b;
else if (typeof(T) == typeof(double))
Vector4D<double>.multiply = (a, b) => a*b;
else if (typeof(T) == typeof(float))
Vector4D<float>.multiply = (a, b) => a*b;
else
multiply = (a, b) =>
{
string message = string.Format("Vector4D<{0}> not supported.", typeof(T));
throw new NotSupportedException(message);
};
}
}
}
こうすることで、すべての乗算 (およびおそらく除算、加算、減算) ロジックを 2 番目の部分構造体に配置し、すべてをメイン ロジックから分離することができます。
2 番目の部分構造体には、構造体の作成に使用される型 T ごとに (アセンブリ ドメインごとに) 1 回だけ呼び出される静的型コンストラクターのみが含まれます。
型をクエリするオーバーヘッドはありますが、プログラムの実行ごとに 1 回だけであり、オーバーヘッドはかなり低いと思います。
また、部分構造体を使用する必要はまったくありません。静的型コンストラクターを構造体実装の残りの部分に配置するだけです。構造体の残りのロジックとは別に考えることができる純粋な初期化ロジックであるため、例としてのみ分離しました。
重要乗算演算を定義していない型で Vector4D を使用すると、 でNotSupportedException
定義された が得られることに注意してくださいstatic Vector4D()
。これは、少なくとも次の行に沿って、何が間違っているかを正確に伝えます。
Unhandled Exception: System.NotSupportedException: Vector4D<System.String> not supported.