ジェネリック(ジェネリクス)
様々な型に対応する関数(やクラス)を定義することができます。
例えば、以下の様な関数があったとします。
Int32score=5;Int32old_score=3;publicstaticInt32GetHighestScore(){return(score>old_score)?score:old_score;}万が一、scoreとold_scoreがdoubleやfloatであった場合、
以下の様に別で関数定義する必要があります。
publicstaticInt32GetHighestScore(){return(score>old_score)?score:old_score;}doublescore=5;doubleold_score=3;publicstaticdoubleGetHighestScoreA(){return(score>old_score)?score:old_score;}といったように、複数のパターンを想定した関数を定義することで解決します。
しかしながら、これは悪い例です。
ハードコーディングですし、プログラムのメンテナンス性(保守性)も最悪です。
これを解決してくれるのが、ジェネリックです。
今回の場合、以下の様に関数定義を行います。
staticInt32score=5;staticInt32old_score=3;publicstaticunsafeTGetHighestScore<T>(Tone,Ttwo)whereT:unmanaged,IComparable{if(one.CompareTo(two)>0){returnone;}returntwo;}ここでいう、where T : unmanagedは、型がアンマネージド型であることを条件にするという意味です。
アンマネージド型とは
sbytebyte、short、int、uint、long、ulong、char、float、double、decimal、bool
がアンマネージド型です。
アンマネージド型 (C# リファレンス)
実例
実例として、MarshalのStructureToPtrをメソッドとして簡易化してみます。
(※Marshal.StructureToPtr()もジェネリック型として定義されていますね!)
publicstructmyStruct{publicintx;publicinty;}staticvoidMain(string[]args){Console.Title="ジェネリック";myStructms=newmyStruct();varpStructure=StructureToPointer<myStruct>(ms);Console.WriteLine("構造体ポインタ: "+(Int32)(pStructure));Console.ReadKey();}////////////////////////////////////////////////////////////////////////// 構造体のマーシャリング////////////////////////////////////////////////////////////////////////publicunsafestaticIntPtrStructureToPointer<T>(T構造体)whereT:struct{varsize=Marshal.SizeOf(構造体.GetType());IntPtrpStructure=Marshal.AllocCoTaskMem(size);//アンマネージドメモリの確保Marshal.StructureToPtr(構造体,pStructure,false);//確保した領域に構造体ポインタを展開Marshal.DestroyStructure(pStructure,typeof(T));//後処理Marshal.FreeCoTaskMem(pStructure);//確保したアンマネージドメモリの解放returnpStructure;}構造体から構造体ポインタへのマーシャリングを簡易化する関数です。
ここでは、where T : structとし、型がstructであることを条件とします。
本来、マネージド型である(アンマネージド型ではない)ジェネリック関数ではsizeof()を使用することができません。(sizeof()がアンマネージド型にのみ対応している為)
なので、Marshal.Sizeof()を使用します。
