値型と参照型で同名の関数を定義したい
通常、C#では同じシグネチャで型制約が値型か参照型かだけが異なる関数を定義できない。
classSample{publicTGetValue<T>(stringkey)whereT:struct{returndefault;}publicTGetValue<T>(stringkey)whereT:class{returndefault;}}に対しては次のようなエラーが出る。
型'Sample'は、'GetValue'と呼ばれるメンバーを同じパラメーターの型で既に定義しています
なぜかというとC#の型制約はシグネチャには含まれないから。
ハック
型制約をシグネチャで代用する。手を抜くなら次のような方法が楽。
classSample{publicTGetValue<T>(stringkey)whereT:struct{returndefault;}publicTGetValue<T>(stringkey,booldummy=false)whereT:class{returndefault;}}見た目が悪いがこれでも引数の違いによりオーバーロードができる。
あるいは
publicclassRequireClass<TClass>whereTClass:class{}publicclassRequireStruct<TStruct>whereTStruct:struct{}を定義したうえで
classSampleClass{publicTGetValue<T>(stringkey,RequireStruct<T>helper=null)whereT:struct{returndefault;}publicTGetValue<T>(stringkey,RequireClass<T>helper=null)whereT:class{returndefault;}}のようにシグネチャで型制約を表現すると引数の型から目的が読み取れて(本当か?)少しマシかもしれない。
もちろんどちらの方法でも値型の戻り値はT?にしてもよい。C#のシグネチャには戻り値の型は含まれないからだ。
いつ使うハックなのか
System.Collections.Hashtable(非ジェネリック版のDictionary)を使ったライブラリをラップするときに使った。
参考
Generic constraints, where T : struct and where T : class - stackoverflow