通過泛型方法定義具有特定類型意義的方法是常用的手段。但在某些特定情況下,例如在一些通用的架構中,直到運作時才能确定泛型類型參數,就必須通過非泛型方式來調用泛型方法。
假定有這樣一個方法:
public static void Add < T > (T obj, IList < T > list)
{
list.Add(obj);
}
如果想換成這樣調用:
Add(Type type, object obj, object list);
通常的方法是這樣的:
void Add(Type type, object obj, object list)
MethodInfo mi = typeof(MyType).GetMethod("Add");
MethodInfo gmi = mi.MakeGenericMethod(type);
gmi.Invoke(new object[]
{ obj, list });
當然,除了性能上的問題,這個方案是完全可行的。但是經過測試,通過這種包裝後的耗時比直接的泛型調用相差将近1000倍。是以還需要一些折中一點的方案。為此,我請教了裝配腦袋。他給出了一個非常好的方案:
先定義一個泛型包裝委托:
public delegate void GM<T>(T obj, IList<T> list);
然後再定義一個非泛型包裝的接口:
interface ING
void NGM(object obj, object list);
然後再實作這個接口,在實作類中直接調用傳入的泛型委托:
public class GClass < T > : ING
private GM<T> m_gmd;
public GClass(GM<T> gmd)
m_gmd = gmd;
}
INGClass 成員#region INGClass 成員
public void NGM(object obj, object list)
m_gmd((T)obj, (IList<T>)list);
#endregion
然後就可以非常簡單地使用已有的泛型方法來獲得一個非泛型接口實作了:
static ING GetNGC(Type genericType, Type methodType, string methodName)
MethodInfo mi = methodType.GetMethod(methodName);
MethodInfo gmi = mi.MakeGenericMethod(genericType);
Delegate gmd = Delegate.CreateDelegate(typeof(GM<>).MakeGenericType(genericType), gmi);
return Activator.CreateInstance(typeof(GClass<>).MakeGenericType(genericType), gmd) as ING;
通過執行所傳回接口的非泛型方法來達到調用泛型方法的目的:
ING ng = GetNGC( typeof ( int ), typeof (MyType), " Add " );
ng.NGM(i, list);
比對一下,耗時大約是直接泛型調用耗時的三倍。顯然這個方案是一個非常實用的方案。歸納一下,一共需要四步:
定義泛型委托;
定義非泛型接口;
實作這個接口;
通過泛型委托擷取非泛型接口的實作。
其中前兩步比較簡單,後兩部稍嫌麻煩。于是,我們再進一步實作一個通用的接口實作及其輸出。
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
namespace GenericMethodTest
/**//// <summary>
/// 接口生成器
/// </summary>
internal static class InterfaceGenerator
private static Random _Random = new Random();
private static char GetRandomLetter()
int i = (_Random.Next() % 26) + 97;
byte[] b = BitConverter.GetBytes(i);
return BitConverter.ToChar(b, 0);
}
private static string GetRandomString(int n)
char[] chars = new char[n];
for (int i = 0; i < n; i++)
chars[i] = GetRandomLetter();
}
return new string(chars);
private static void LoadArg(ILGenerator gen, int index)
switch (index)
case 0:
gen.Emit(OpCodes.Ldarg_0);
break;
case 1:
gen.Emit(OpCodes.Ldarg_1);
case 2:
gen.Emit(OpCodes.Ldarg_2);
case 3:
gen.Emit(OpCodes.Ldarg_3);
default:
if (index < 128)
gen.Emit(OpCodes.Ldarg_S, index);
}
else
gen.Emit(OpCodes.Ldarg, index);
public static T GetInterface<T>(Delegate GM)
if (typeof(T).IsInterface)
Type delegateType = GM.GetType();
if (delegateType.IsGenericType)
if (typeof(MulticastDelegate).IsAssignableFrom(delegateType.GetGenericTypeDefinition()))
Type[] genericTypes = delegateType.GetGenericArguments();
if (genericTypes.Length == 1)
Type genericType = genericTypes[0];
#if SAVE
string theFilename = "InterfaceGenerator.Attachments.dll";
#endif
AssemblyName aname = new AssemblyName();
aname.Name = string.Format("InterfaceGenerator.Attachments.{0}", GetRandomString(16));
aname.Version = new Version("2.0.0.0");
AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(aname,
AssemblyBuilderAccess.RunAndSave
#else
AssemblyBuilderAccess.Run
);
ModuleBuilder module = assembly.DefineDynamicModule(GetRandomString(8)
, theFilename
TypeBuilder builder = module.DefineType(GetRandomString(16), TypeAttributes.Sealed | TypeAttributes.Class | TypeAttributes.Public);
builder.AddInterfaceImplementation(typeof(T));
// 先定義成員域,用于儲存傳入的委托。
FieldBuilder field = builder.DefineField(GetRandomString(8), delegateType, FieldAttributes.Private);
// 定義構造器。
ConstructorBuilder ctor = builder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[]
{ delegateType });
ILGenerator ctorGen = ctor.GetILGenerator();
ctorGen.Emit(OpCodes.Ldarg_0);
ctorGen.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[]
{ }));
ctorGen.Emit(OpCodes.Ldarg_1);
ctorGen.Emit(OpCodes.Stfld, field);
ctorGen.Emit(OpCodes.Ret);
// 雖然這麼寫,但事實上接口隻有一個方法。
foreach (MethodInfo bmi in typeof(T).GetMethods())
ParameterInfo[] paramInfos = bmi.GetParameters();
Type[] argTypes = new Type[paramInfos.Length];
int i = 0;
foreach (ParameterInfo pi in paramInfos)
argTypes[i++] = pi.ParameterType;
}
MethodAttributes attributes = MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.ReuseSlot | MethodAttributes.Public;
MethodBuilder method = builder.DefineMethod(bmi.Name, attributes, bmi.ReturnType, argTypes);
builder.DefineMethodOverride(method, bmi);
MethodInfo dmi = delegateType.GetMethod("Invoke");
ILGenerator methodGen = method.GetILGenerator();
bool hasReturn = false;
if (dmi.ReturnType != typeof(void))
methodGen.DeclareLocal(dmi.ReturnType);
hasReturn = true;
methodGen.Emit(OpCodes.Ldarg_0);
methodGen.Emit(OpCodes.Ldfld, field);
i = 0;
foreach (ParameterInfo pi in dmi.GetParameters())
LoadArg(methodGen, i + 1);
if (!pi.ParameterType.IsAssignableFrom(argTypes[i]))
if (argTypes[i].IsClass)
methodGen.Emit(OpCodes.Castclass, pi.ParameterType);
}
else
methodGen.Emit(OpCodes.Unbox, pi.ParameterType);
}
i++;
methodGen.Emit(OpCodes.Callvirt, dmi);
if (hasReturn)
methodGen.Emit(OpCodes.Stloc_0);
methodGen.Emit(OpCodes.Ldloc_0);
methodGen.Emit(OpCodes.Ret);
}
Type target = builder.CreateType();
assembly.Save(theFilename);
ConstructorInfo ci = target.GetConstructor(new Type[]
return (T) ci.Invoke(new object[]
{ GM });
}
}
return default(T);
結論:
以下是測試代碼:
// 為泛型方法定義的委托
public delegate void GM<T>(T obj, IList<T> list);
// 為非泛型方法定義的接口
public interface ING
void NGM(object obj, object list);
class Program
static void Main(string[] args)
List<int> list = new List<int>();
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Reset();
watch.Start();
for (int i = 0; i < 1000000; i++)
list.Add(i);
watch.Stop();
long l1 = watch.ElapsedMilliseconds;
GM<int> gm = new GM<int>(Program.Add);
gm(i, list);
long l2 = watch.ElapsedMilliseconds;
MethodInfo mi = typeof(Program).GetMethod("Add");
MethodInfo gmi = mi.MakeGenericMethod(typeof(int));
gmi.Invoke(null, new object[]
{ i, list });
long l3 = watch.ElapsedMilliseconds;
ING ng1 = GetNGC(typeof(int), typeof(Program), "Add");
ng1.NGM(i, list);
long l4 = watch.ElapsedMilliseconds;
ING ng2 = InterfaceGenerator.GetInterface<ING>(new GM<int>(Program.Add));
ng2.NGM(i, list);
long l5 = watch.ElapsedMilliseconds;
Console.WriteLine("{0}/n{1} vs {2} vs {3} vs {4} vs {5}", list.Count, l1, l2, l3, l4, l5);
Console.ReadLine();
public static void Add<T>(T obj, IList<T> list)
list.Add(obj);
static ING GetNGC(Type genericType, Type methodType, string methodName)
MethodInfo mi = methodType.GetMethod(methodName);
MethodInfo gmi = mi.MakeGenericMethod(genericType);
Delegate gmd = Delegate.CreateDelegate(typeof(GM<>).MakeGenericType(genericType), gmi);
return Activator.CreateInstance(typeof(GClass<>).MakeGenericType(genericType), gmd) as ING;
public class GClass<T> : ING
private GM<T> m_gmd;
public GClass(GM<T> gmd)
m_gmd = gmd;
INGClass 成員#region INGClass 成員
public void NGM(object obj, object list)
m_gmd((T)obj, (IList<T>)list);
#endregion
測試結果:
方案
耗時
比對
其他優點
直接調用
18
1
不通用
泛型委托包裝
43
2.39
反射
16538
918.78
通用,不需額外定義
非泛型接口包裝
60
3.33
通用,需要額外定義并實作
動态生成的非泛型接口包裝
72
4
通用,需要額外定義