以非泛型方式调用泛型方法

通过泛型方法定义具有特定类型意义的方法是常用的手段。但在某些特定情况下,例如在一些通用的框架中,直到运行时才能确定泛型类型参数,就必须通过非泛型方式来调用泛型方法。
假定有这样一个方法:
None.gif public   static   void  Add < T > (T obj, IList < T >  list)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif      list.Add(obj);
ExpandedBlockEnd.gif}

None.gif
如果想换成这样调用:
None.gif Add(Type type,  object  obj,  object  list);
通常的方法是这样的:
None.gif void  Add(Type type,  object  obj,  object  list)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    MethodInfo mi 
= typeof(MyType).GetMethod("Add");
InBlock.gif    MethodInfo gmi 
= mi.MakeGenericMethod(type);
ExpandedSubBlockStart.gifContractedSubBlock.gif    gmi.Invoke(
new object[] dot.gif{ obj, list });
ExpandedBlockEnd.gif}
当然,除了性能上的问题,这个方案是完全可行的。但是经过测试,通过这种包装后的耗时比直接的泛型调用相差将近1000倍。因此还需要一些折中一点的方案。为此,我请教了装配脑袋。他给出了一个非常好的方案:
先定义一个泛型包装委托:
None.gif public   delegate   void  GM < T > (T obj, IList < T >  list);
然后再定义一个非泛型包装的接口:
None.gif interface  ING
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
void NGM(object obj, object list);
ExpandedBlockEnd.gif}
然后再实现这个接口,在实现类中直接调用传入的泛型委托:
None.gif public   class  GClass < T >  : ING
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
private GM<T> m_gmd;
InBlock.gif
InBlock.gif    
public GClass(GM<T> gmd)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        m_gmd 
= gmd;
ExpandedSubBlockEnd.gif    }

InBlock.gif
ContractedSubBlock.gifExpandedSubBlockStart.gif    
INGClass 成员#region INGClass 成员
InBlock.gif
InBlock.gif    
public void NGM(object obj, object list)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        m_gmd((T)obj, (IList
<T>)list);
ExpandedSubBlockEnd.gif    }

InBlock.gif
ExpandedSubBlockEnd.gif    
#endregion

ExpandedBlockEnd.gif}
然后就可以非常简单地使用已有的泛型方法来获得一个非泛型接口实现了:
None.gif static  ING GetNGC(Type genericType, Type methodType,  string  methodName)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    MethodInfo mi 
= methodType.GetMethod(methodName);
InBlock.gif    MethodInfo gmi 
= mi.MakeGenericMethod(genericType);
InBlock.gif    Delegate gmd 
= Delegate.CreateDelegate(typeof(GM<>).MakeGenericType(genericType), gmi);
InBlock.gif    
return Activator.CreateInstance(typeof(GClass<>).MakeGenericType(genericType), gmd) as ING;
ExpandedBlockEnd.gif}

None.gif
通过执行所返回接口的非泛型方法来达到调用泛型方法的目的:
None.gif ING ng  =  GetNGC( typeof ( int ),  typeof (MyType),  " Add " );
None.gifng.NGM(i, list);
比对一下,耗时大约是直接泛型调用耗时的三倍。显然这个方案是一个非常实用的方案。归纳一下,一共需要四步:
  • 定义泛型委托;
  • 定义非泛型接口;
  • 实现这个接口;
  • 通过泛型委托获取非泛型接口的实现。
其中前两步比较简单,后两部稍嫌麻烦。于是,我们再进一步实现一个通用的接口实现及其输出。
None.gif using  System;
None.gif
using  System.Collections.Generic;
None.gif
using  System.Reflection;
None.gif
using  System.Reflection.Emit;
None.gif
using  System.Text;
None.gif
None.gif
namespace  GenericMethodTest
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**//// <summary>
InBlock.gif    
/// 接口生成器
ExpandedSubBlockEnd.gif    
/// </summary>

InBlock.gif    internal static class InterfaceGenerator
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
private static Random _Random = new Random();
InBlock.gif
InBlock.gif        
private static char GetRandomLetter()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
int i = (_Random.Next() % 26+ 97;
InBlock.gif            
byte[] b = BitConverter.GetBytes(i);
InBlock.gif            
return BitConverter.ToChar(b, 0);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
private static string GetRandomString(int n)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
char[] chars = new char[n];
InBlock.gif            
for (int i = 0; i < n; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                chars[i] 
= GetRandomLetter();
ExpandedSubBlockEnd.gif            }

InBlock.gif            
return new string(chars);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
private static void LoadArg(ILGenerator gen, int index)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
switch (index)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
case 0:
InBlock.gif                    gen.Emit(OpCodes.Ldarg_0);
InBlock.gif                    
break;
InBlock.gif                
case 1:
InBlock.gif                    gen.Emit(OpCodes.Ldarg_1);
InBlock.gif                    
break;
InBlock.gif                
case 2:
InBlock.gif                    gen.Emit(OpCodes.Ldarg_2);
InBlock.gif                    
break;
InBlock.gif                
case 3:
InBlock.gif                    gen.Emit(OpCodes.Ldarg_3);
InBlock.gif                    
break;
InBlock.gif                
default:
InBlock.gif                    
if (index < 128)
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        gen.Emit(OpCodes.Ldarg_S, index);
ExpandedSubBlockEnd.gif                    }

InBlock.gif                    
else
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        gen.Emit(OpCodes.Ldarg, index);
ExpandedSubBlockEnd.gif                    }

InBlock.gif                    
break;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public static T GetInterface<T>(Delegate GM)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if (typeof(T).IsInterface)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                Type delegateType 
= GM.GetType();
InBlock.gif                
if (delegateType.IsGenericType)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
if (typeof(MulticastDelegate).IsAssignableFrom(delegateType.GetGenericTypeDefinition()))
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        Type[] genericTypes 
= delegateType.GetGenericArguments();
InBlock.gif                        
if (genericTypes.Length == 1)
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
dot.gif{
InBlock.gif                            Type genericType 
= genericTypes[0];
InBlock.gif
InBlock.gif
#if SAVE
InBlock.gif                            
string theFilename = "InterfaceGenerator.Attachments.dll";
InBlock.gif
#endif
InBlock.gif                            AssemblyName aname 
= new AssemblyName();
InBlock.gif                            aname.Name 
= string.Format("InterfaceGenerator.Attachments.{0}", GetRandomString(16));
InBlock.gif                            aname.Version 
= new Version("2.0.0.0");
InBlock.gif                            AssemblyBuilder assembly 
= AppDomain.CurrentDomain.DefineDynamicAssembly(aname,
InBlock.gif
#if SAVE
InBlock.gif AssemblyBuilderAccess.RunAndSave
InBlock.gif
#else
InBlock.gif AssemblyBuilderAccess.Run
InBlock.gif
#endif
InBlock.gif);
InBlock.gif                            ModuleBuilder module 
= assembly.DefineDynamicModule(GetRandomString(8)
InBlock.gif
#if SAVE
InBlock.gif, theFilename
InBlock.gif
#endif
InBlock.gif);
InBlock.gif                            TypeBuilder builder 
= module.DefineType(GetRandomString(16), TypeAttributes.Sealed | TypeAttributes.Class | TypeAttributes.Public);
InBlock.gif                            builder.AddInterfaceImplementation(
typeof(T));
InBlock.gif
InBlock.gif                            
// 先定义成员域,用于保存传入的委托。
InBlock.gif
                            FieldBuilder field = builder.DefineField(GetRandomString(8), delegateType, FieldAttributes.Private);
InBlock.gif
InBlock.gif                            
// 定义构造器。
ExpandedSubBlockStart.gifContractedSubBlock.gif
                            ConstructorBuilder ctor = builder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] dot.gif{ delegateType });
InBlock.gif                            ILGenerator ctorGen 
= ctor.GetILGenerator();
InBlock.gif                            ctorGen.Emit(OpCodes.Ldarg_0);
ExpandedSubBlockStart.gifContractedSubBlock.gif                            ctorGen.Emit(OpCodes.Call, 
typeof(object).GetConstructor(new Type[] dot.gif{ }));
InBlock.gif                            ctorGen.Emit(OpCodes.Ldarg_0);
InBlock.gif                            ctorGen.Emit(OpCodes.Ldarg_1);
InBlock.gif                            ctorGen.Emit(OpCodes.Stfld, field);
InBlock.gif                            ctorGen.Emit(OpCodes.Ret);
InBlock.gif
InBlock.gif                            
// 虽然这么写,但事实上接口只有一个方法。
InBlock.gif
                            foreach (MethodInfo bmi in typeof(T).GetMethods())
ExpandedSubBlockStart.gifContractedSubBlock.gif                            
dot.gif{
InBlock.gif                                ParameterInfo[] paramInfos 
= bmi.GetParameters();
InBlock.gif                                Type[] argTypes 
= new Type[paramInfos.Length];
InBlock.gif                                
int i = 0;
InBlock.gif                                
foreach (ParameterInfo pi in paramInfos)
ExpandedSubBlockStart.gifContractedSubBlock.gif                                
dot.gif{
InBlock.gif                                    argTypes[i
++= pi.ParameterType;
ExpandedSubBlockEnd.gif                                }

InBlock.gif                                MethodAttributes attributes 
= MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.ReuseSlot | MethodAttributes.Public;
InBlock.gif                                MethodBuilder method 
= builder.DefineMethod(bmi.Name, attributes, bmi.ReturnType, argTypes);
InBlock.gif                                builder.DefineMethodOverride(method, bmi);
InBlock.gif                                MethodInfo dmi 
= delegateType.GetMethod("Invoke");
InBlock.gif                                ILGenerator methodGen 
= method.GetILGenerator();
InBlock.gif                                
bool hasReturn = false;
InBlock.gif                                
if (dmi.ReturnType != typeof(void))
ExpandedSubBlockStart.gifContractedSubBlock.gif                                
dot.gif{
InBlock.gif                                    methodGen.DeclareLocal(dmi.ReturnType);
InBlock.gif                                    hasReturn 
= true;
ExpandedSubBlockEnd.gif                                }

InBlock.gif                                methodGen.Emit(OpCodes.Ldarg_0);
InBlock.gif                                methodGen.Emit(OpCodes.Ldfld, field);
InBlock.gif
InBlock.gif                                i 
= 0;
InBlock.gif                                
foreach (ParameterInfo pi in dmi.GetParameters())
ExpandedSubBlockStart.gifContractedSubBlock.gif                                
dot.gif{
InBlock.gif                                    LoadArg(methodGen, i 
+ 1);
InBlock.gif                                    
if (!pi.ParameterType.IsAssignableFrom(argTypes[i]))
ExpandedSubBlockStart.gifContractedSubBlock.gif                                    
dot.gif{
InBlock.gif                                        
if (argTypes[i].IsClass)
ExpandedSubBlockStart.gifContractedSubBlock.gif                                        
dot.gif{
InBlock.gif                                            methodGen.Emit(OpCodes.Castclass, pi.ParameterType);
ExpandedSubBlockEnd.gif                                        }

InBlock.gif                                        
else
ExpandedSubBlockStart.gifContractedSubBlock.gif                                        
dot.gif{
InBlock.gif                                            methodGen.Emit(OpCodes.Unbox, pi.ParameterType);
ExpandedSubBlockEnd.gif                                        }

ExpandedSubBlockEnd.gif                                    }

InBlock.gif                                    i
++;
ExpandedSubBlockEnd.gif                                }

InBlock.gif                                methodGen.Emit(OpCodes.Callvirt, dmi);
InBlock.gif                                
if (hasReturn)
ExpandedSubBlockStart.gifContractedSubBlock.gif                                
dot.gif{
InBlock.gif                                    methodGen.Emit(OpCodes.Stloc_0);
InBlock.gif                                    methodGen.Emit(OpCodes.Ldloc_0);
ExpandedSubBlockEnd.gif                                }

InBlock.gif                                methodGen.Emit(OpCodes.Ret);
ExpandedSubBlockEnd.gif                            }

InBlock.gif                            Type target 
= builder.CreateType();
InBlock.gif
#if SAVE
InBlock.gif                            assembly.Save(theFilename);
InBlock.gif
#endif
ExpandedSubBlockStart.gifContractedSubBlock.gif                            ConstructorInfo ci 
= target.GetConstructor(new Type[] dot.gif{ delegateType });
ExpandedSubBlockStart.gifContractedSubBlock.gif                            
return (T) ci.Invoke(new object[] dot.gif{ GM });
ExpandedSubBlockEnd.gif                        }

ExpandedSubBlockEnd.gif                    }

ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

InBlock.gif            
return default(T);
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif

结论:
以下是测试代码:

None.gif using  System;
None.gif
using  System.Collections.Generic;
None.gif
using  System.Reflection;
None.gif
using  System.Text;
None.gif
None.gif
namespace  GenericMethodTest
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
// 为泛型方法定义的委托
InBlock.gif
    public delegate void GM<T>(T obj, IList<T> list);
InBlock.gif
InBlock.gif    
// 为非泛型方法定义的接口
InBlock.gif
    public interface ING
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
void NGM(object obj, object list);
ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
class Program
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
static void Main(string[] args)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            List
<int> list = new List<int>();
InBlock.gif            System.Diagnostics.Stopwatch watch 
= new System.Diagnostics.Stopwatch();
InBlock.gif            watch.Reset();
InBlock.gif            watch.Start();
InBlock.gif            
for (int i = 0; i < 1000000; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                list.Add(i);
ExpandedSubBlockEnd.gif            }

InBlock.gif            watch.Stop();
InBlock.gif            
long l1 = watch.ElapsedMilliseconds;
InBlock.gif            watch.Reset();
InBlock.gif            watch.Start();
InBlock.gif            GM
<int> gm = new GM<int>(Program.Add);
InBlock.gif            
for (int i = 0; i < 1000000; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                gm(i, list);
ExpandedSubBlockEnd.gif            }

InBlock.gif            watch.Stop();
InBlock.gif            
long l2 = watch.ElapsedMilliseconds;
InBlock.gif            watch.Reset();
InBlock.gif            watch.Start();
InBlock.gif            MethodInfo mi 
= typeof(Program).GetMethod("Add");
InBlock.gif            MethodInfo gmi 
= mi.MakeGenericMethod(typeof(int));
InBlock.gif            
for (int i = 0; i < 1000000; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif                gmi.Invoke(
nullnew object[] dot.gif{ i, list });
ExpandedSubBlockEnd.gif            }

InBlock.gif            watch.Stop();
InBlock.gif            
long l3 = watch.ElapsedMilliseconds;
InBlock.gif            watch.Reset();
InBlock.gif            watch.Start();
InBlock.gif            ING ng1 
= GetNGC(typeof(int), typeof(Program), "Add");
InBlock.gif            
for (int i = 0; i < 1000000; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                ng1.NGM(i, list);
ExpandedSubBlockEnd.gif            }

InBlock.gif            watch.Stop();
InBlock.gif            
long l4 = watch.ElapsedMilliseconds;
InBlock.gif            watch.Reset();
InBlock.gif            watch.Start();
InBlock.gif            ING ng2 
= InterfaceGenerator.GetInterface<ING>(new GM<int>(Program.Add));
InBlock.gif            
for (int i = 0; i < 1000000; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                ng2.NGM(i, list);
ExpandedSubBlockEnd.gif            }

InBlock.gif            watch.Stop();
InBlock.gif            
long l5 = watch.ElapsedMilliseconds;
InBlock.gif            Console.WriteLine(
"{0}\n{1} vs {2} vs {3} vs {4} vs {5}", list.Count, l1, l2, l3, l4, l5);
InBlock.gif            Console.ReadLine();
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public static void Add<T>(T obj, IList<T> list)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            list.Add(obj);
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
static ING GetNGC(Type genericType, Type methodType, string methodName)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            MethodInfo mi 
= methodType.GetMethod(methodName);
InBlock.gif            MethodInfo gmi 
= mi.MakeGenericMethod(genericType);
InBlock.gif            Delegate gmd 
= Delegate.CreateDelegate(typeof(GM<>).MakeGenericType(genericType), gmi);
InBlock.gif            
return Activator.CreateInstance(typeof(GClass<>).MakeGenericType(genericType), gmd) as ING;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
public class GClass<T> : ING
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
private GM<T> m_gmd;
InBlock.gif
InBlock.gif        
public GClass(GM<T> gmd)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            m_gmd 
= gmd;
ExpandedSubBlockEnd.gif        }

InBlock.gif
ContractedSubBlock.gifExpandedSubBlockStart.gif        
INGClass 成员#region INGClass 成员
InBlock.gif
InBlock.gif        
public void NGM(object obj, object list)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            m_gmd((T)obj, (IList
<T>)list);
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockEnd.gif        
#endregion

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif

测试结果:

方案耗时比对其他优点
直接调用181不通用
泛型委托包装432.39不通用
反射16538918.78通用,不需额外定义
非泛型接口包装603.33通用,需要额外定义并实现
动态生成的非泛型接口包装724通用,需要额外定义
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值