前書き
この記事は連載の一部となっています。連載インデックスは 【ILP 連載】自分で ILP を実装してみよう! をご覧下さい。
今回もややライトな記事になります。
ILP の仕様を理解するために、Hyperledger Quilt の Java で書かれたコードを C# に移植していっていますが、OER のエンコード・デコードをするためにジェネリクスが多用されたコードとなっており、Java と C# でジェネリクスまわりの思想が違うためにやや苦労しています。Java の方は Type Erasure の思想、C# の方は Reification の思想という事で、一筋縄には行かない事態となっております(汗) まぁ、全部 C# に合う様に書き直してしまえばいいんでしょうが、まだ本家の方にも色々修正が入りそうですし、C# の方だけ構造が違うと他の人も見にくいでしょうし、できるだけ一致させておきたい気はしています。
そんな訳で、必要に駆られて C# の Type まわりの仕様について少し勉強してみました。特に、ジェネリックなインターフェースを持つかどうかをどうやって判定したらいいのかという部分について、調べてみた記事になります。
ジェネリックなインターフェースを持つかどうかを判定するにはどうすればいいのか
例えば、 IGenericInterface<T>
があったとします。これを実装したクラス、 GenericInterfaceClass : IGenericInterface<object>
があったとします。この時、 GenericInterfaceClass
が IGenericInterface<T>
を持つかどうかを判定するにはどうしたらよいでしょうか?
結論としては、GetGenericTypeDefinition()
を使え という事になりますが、サンプルコードを添付しておきます。
using System;
namespace CSType
{
public class CSType
{
public static void Main(string[] args)
{
CSType csType = new CSType();
csType.Types();
}
public void Types()
{
Type nonGenericInterfaceType = typeof(INonGenericInterface);
Console.WriteLine(string.Format("Type of non generic interface: {0}", nonGenericInterfaceType));
// Type of non generic interface: CSType.INonGenericInterface
NonGenericInterfaceClass nonGenericInterfaceClass = new NonGenericInterfaceClass();
Type nonGenericInterfaceClassType = nonGenericInterfaceClass.GetType();
// Interface を実装したクラスは当然 is で判定可能
Console.WriteLine(string.Format("{0} is {1}? {2}", nonGenericInterfaceClass, nonGenericInterfaceType, nonGenericInterfaceClass is INonGenericInterface));
// CSType.NonGenericInterfaceClass is CSType.INonGenericInterface? True
// Interface を実装したクラスの Type からインターフェースを取り出してみる
foreach (Type interfaceType in nonGenericInterfaceClassType.GetInterfaces())
{
Console.WriteLine(string.Format("Interface type of {0}: {1}", nonGenericInterfaceClassType, interfaceType));
// Interface type of CSType.NonGenericInterfaceClass: CSType.INonGenericInterface
}
SubClassOfNonGenericInterfaceClass subClassOfNonGenericInterfaceClass = new SubClassOfNonGenericInterfaceClass();
Type subClassOfNonGenericInterfaceClassType = subClassOfNonGenericInterfaceClass.GetType();
// 例えそのクラス自体がインターフェースを定義していなくても
// インターフェースを実装したクラスのサブクラスであればインターフェースを取得できる事が分かる
foreach (Type interfaceType in subClassOfNonGenericInterfaceClassType.GetInterfaces())
{
Console.WriteLine(string.Format("Interface type of {0}: {1}", subClassOfNonGenericInterfaceClassType, interfaceType));
// Interface type of CSType.SubClassOfNonGenericInterfaceClass: CSType.INonGenericInterface
}
// そもそもこういう書き方できるんだね、っていうw
// しかし、genericInterfaceClass is IGenericInterface<> とは書けない
Type genericInterfaceType = typeof(IGenericInterface<>);
Console.WriteLine(string.Format("Type of generic interface: {0}", genericInterfaceType));
// Type of generic interface: CSType.IGenericInterface`1[T]
GenericInterfaceClass genericInterfaceClass = new GenericInterfaceClass();
Type genericInterfaceClassType = genericInterfaceClass.GetType();
// Interface を実装したクラスの Type からインターフェースを取り出してみる
foreach (Type interfaceType in genericInterfaceClassType.GetInterfaces())
{
Console.WriteLine(string.Format("Interface type of {0}: {1}", genericInterfaceClassType, interfaceType));
Console.WriteLine(string.Format("{0} is {1} ? {2}", genericInterfaceType, interfaceType, genericInterfaceType == interfaceType));
Console.WriteLine(string.Format("{0} is {1} ? {2}", genericInterfaceType.GetGenericTypeDefinition(), interfaceType.GetGenericTypeDefinition(), genericInterfaceType.GetGenericTypeDefinition() == interfaceType.GetGenericTypeDefinition()));
// Interface type of CSType.GenericInterfaceClass: CSType.IGenericInterface`1[System.Object]
// CSType.IGenericInterface`1[T] is CSType.IGenericInterface`1[System.Object] ? False
// CSType.IGenericInterface`1[T] is CSType.IGenericInterface`1[T] ? True
// ↑これやー!
}
}
}
}
という訳で、 GetGenericTypeDefinition
を使うとうまくいきました。サンプルコードは GitHub のリポジトリ にも置いておきました。
それではみなさま、よい ILP ライフを!