今回のネタはEnumerableの拡張メソッドをGetMethodしたい から。
こういう拡張メソッドはどうでしょうか。Tuple<T1, T2> クラスはサンプルのために作った型引数を二つ取る適当なクラスなので、KeyValuePair<TKey, TValue> などで置き換えできます。ちなみに拡張メソッドにせず一部の Enumerale のメソッドを手で書けば .NET 2.0 以上で動くはずです。
// 使い方publicclass Sample {publicvoid Test<T>(List<T> arg) { }publicvoid Test<T1, T2>(Tuple<T1, T2> arg1, Tuple<Tuple<Int32, Int32>, Tuple<Int32, T2>> arg2) { }staticvoid Main() {var sample =typeof(Sample);// Test<T> のメソッドを取得// メソッド型引数を使った引数型を構築する// 型引数の指定はインデックス(0)または名前("T")でvar openType =new OpenTypeBuilder(typeof(List<>)).Add(0).Build();var method1 = sample.GetMethod("Test", openType); Console.WriteLine(method1);// Test<T1, T2> のメソッドを取得// メソッド型引数を使った引数型の構築が面倒なのは避けられない Type tuple2 =typeof(Tuple<,>), int32 =typeof(Int32);// 一つ目の引数 Tuple<T1, T2> の型を構築var t1t2 =new OpenTypeBuilder(tuple2).Add("T1").Add("T2").Build();// 二つ目の引数型はまずその構成要素の Tuple<Int32, Int32> などから先に構築var intInt =typeof(Tuple<Int32, Int32>);// こちらは静的に Type を作成可能var intT2 =new OpenTypeBuilder(tuple2).Add(int32).Add("T2").Build();// 構成要素二つをもとに二つ目の引数型を構築var tupleTuple =new OpenTypeBuilder(tuple2).Add(intInt).Add(intT2).Build();// 二つの引数型を使ってメソッドを取得var method2 = sample.GetMethod("Test", t1t2, tupleTuple); Console.WriteLine(method2); }}publicstaticclass TypeExtensions {// 特定の名前を持つメソッドの MethodInfo 配列を返すpublicstatic MethodInfo[] GetMethods(this Type type,string name) {return type.GetMethods().Where(method => method.Name == name).ToArray(); }// 特定の名前と flags に合致するメソッドの MethodInfo 配列を返すpublicstatic MethodInfo[] GetMethods(this Type type,string name, BindingFlags flags) {return type.GetMethods(flags).Where(method => method.Name == name).ToArray(); }// 特定の名前と引数型を持つpublic メソッドの MethodInfo を返すpublicstatic MethodInfo GetMethod(this Type type,string name,params GenericType[] argTypes) {return GetMethod(type, name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static, argTypes); }// 特定の名前と引数型を持つメソッドの MethodInfo を返すpublicstatic MethodInfo GetMethod(this Type type,string name, BindingFlags flags,params GenericType[] argTypes) { MethodInfo retval =null;// まず該当する名前のメソッドを列挙するforeach (var methodin GetMethods(type, name)) {// MethodInfo からそのメソッドの型引数の型の配列が手に入るvar methodTypes = method.GetGenericArguments();var parameters = method.GetParameters().Select(p => p.ParameterType).ToArray();// 引数の長さが違ったら絶対別物if (parameters.Length != argTypes.Length)continue;bool isMatch =true;for (int i = 0; i < parameters.Length; ++i) {if (parameters[i] != argTypes[i].GetType(methodTypes)) { isMatch =false;break; } }if (isMatch) {if (retval !=null)thrownew AmbiguousMatchException("あいまいな一致が見つかりました。"); retval = method; } }return retval; }}// 3 タイプの型表現をまとめて扱うための基底抽象クラスpublicabstractclass GenericType {// メソッドの型引数から Type オブジェクトを取得・生成する抽象メソッドpublicabstract Type GetType(Type[] typeArgumentTypes);}// 静的に Type を作成できる型publicsealedclass ClosedType : GenericType {public Type Type {get;privateset; }// コンストラクタ引数が正当かどうかのチェックは省略// type が閉じていること(Type.ContainsGenericParameter)public ClosedType(Type type) {this.Type = type; }// 既に型は作られているのでそれをそのまま返すだけpublicoverride Type GetType(Type[] typeArgumentTypes) {returnthis.Type; }}// メソッド型引数の型 Test<T1, T2>(T1 arg1, T2 arg2) の T1・T2// メソッド型引数内のインデックス、または型引数の名前で識別するpublicsealedclass TypeArgumentType : GenericType {// メソッド型引数内の 0 オリジンのインデックス。T1 なら 0、T2 なら 1publicint TypeArgumentIndex {get;privateset; }// メソッド型引数の名前。T1 なら "T1"。Case-sensitive。publicstring TypeArgumentName {get;privateset; }// 型引数内のインデックスで識別する用のコンストラクタpublic TypeArgumentType(int index) {this.TypeArgumentIndex = index; }// 型引数の名前で識別する用のコンストラクタpublic TypeArgumentType(string name) {this.TypeArgumentName = name;this.TypeArgumentIndex = -1; }// メソッドの型引数型配列内のうち、自分に合致するものを返すpublicoverride Type GetType(Type[] typeArgumentTypes) {if (this.TypeArgumentIndex < 0) {// 名前で識別する場合、Type.Name が合致するかどうかで判断するreturn typeArgumentTypes.FirstOrDefault( type => type.Name ==this.TypeArgumentName); }else {// インデックスで識別する場合、インデックスがオーバーする可能性があるif (this.TypeArgumentIndex >= typeArgumentTypes.Length)returnnull;return typeArgumentTypes[this.TypeArgumentIndex]; } }}// メソッド型引数の型を型引数に持つジェネリック型の型// Test<T1, T2>(Tuple<T1, Tuple<Int32, T2>> arg) の arg の型publicsealedclass OpenType : GenericType {// 型引数が未指定の開いたジェネリック型。typeof(Tuple<,>)public Type BaseType {get;privateset; }// 型引数のリスト。T1 を指す TypeArgumentType や// Tuple<Int32, T2> を指す OpenType などの配列public GenericType[] TypeArguments {get;privateset; }// コンストラクタ引数が正当かどうかのチェックは省略// baseType は一つ以上型引数を持ち全ての型引数が未指定であること// typeArguments は baseType の型引数と同じ要素数であること などpublic OpenType(Type baseType, GenericType[] typeArguments) {this.BaseType = baseType;this.TypeArguments = typeArguments; }// メソッドの型引数型が明らかになったので// それを使ってジェネリック型を再帰的に構築できる// T1 を指す TypeArgumentType なら "T1" や 0 から T1 型を取得できる// Tuple<Int32, T2> を指す OpenType は TypeArguments に// Int32 の ClosedType 及び T2 の TypeArgumentType を持っていて、// その二つはそれぞれ GetType で Int32 型と T2 型を返す。// BaseType の Tuple<,> にこの二つの Type を MakeGenericType することで// Tuple<Int32, T2> を指す Type が作成できる。// 最後に Tuple<,> 及び T1、Tuple<Int32, T2> から// Tuple<T1, Tuple<Int32, T2>> の Type が MakeGenericType で作成される。publicoverride Type GetType(Type[] typeArgumentTypes) {var args =new List<Type>();foreach (var arginthis.TypeArguments) {var argType = arg.GetType(typeArgumentTypes);if (argType ==null)returnnull; args.Add(argType); }returnthis.BaseType.MakeGenericType(args.ToArray()); }}// 直接 OpenType を作るのは面倒なのでヘルパクラスを用意publicclass OpenTypeBuilder {// OpenType の基となる型を指定してインスタンスを生成 引数の正当性は省略// 一つ以上型引数を持ち全ての型引数が未指定であることpublic OpenTypeBuilder(Type baseType) {this.baseType = baseType; }// メソッド型引数のインデックスを指定して型引数型の型引数を追加// 自分自身を返すので builder.Add(0).Add("T1").Build() という使い方が可能public OpenTypeBuilder Add(int index) {this.typeArguments.Add(new TypeArgumentType(index));returnthis; }// メソッド型引数の名前を指定して型引数型の型引数を追加public OpenTypeBuilder Add(string name) {this.typeArguments.Add(new TypeArgumentType(name));returnthis; }// Type を指定して静的な型の型引数を追加public OpenTypeBuilder Add(Type type) {this.typeArguments.Add(new ClosedType(type));returnthis; }// OpenType を指定してメソッド型引数の型を使用するジェネリック型の型引数を追加public OpenTypeBuilder Add(OpenType type) {this.typeArguments.Add(type);returnthis; }// OpenType を生成する 追加した型引数の過不足などのチェックは省略public OpenType Build() {returnnew OpenType(this.baseType,this.typeArguments.ToArray()); }private Type baseType;private List<GenericType> typeArguments =new List<GenericType>();}
ここ(hongliang.seesaa.net)で公開しているものについて、利用は自由に行って頂いて構いません。改変、再頒布もお好きになさって下さい。利用に対しこちらが何かを要求することはありません。
ただし、公開するものを使用、または参考したことによって何らかの損害等が生じた場合でも、私はいかなる責任も負いません。
あ、こんなのに使ったってコメントを頂ければ嬉しいです。
この広告は90日以上新しい記事の投稿がないブログに表示されております。