比对一下,耗时大约是直接泛型调用耗时的三倍。显然这个方案是一个非常实用的方案。归纳一下,一共需要四步:
  • 定义泛型委托;
  • 定义非泛型接口;
  • 实现这个接口;
  • 通过泛型委托获取非泛型接口的实现。
其中前两步比较简单,后两部稍嫌麻烦。于是,我们再进一步实现一个通用的接口实现及其输出。
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.gif
{
ExpandedSubBlockStart.gif    
/// <summary>
InBlock.gif    
/// 接口生成器
ExpandedSubBlockEnd.gif    
/// </summary>

InBlock.gif    internal static class InterfaceGenerator
ExpandedSubBlockStart.gif    
{
InBlock.gif        
private static Random _Random = new Random();
InBlock.gif
InBlock.gif        
private static char GetRandomLetter()
ExpandedSubBlockStart.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.gif        
{
InBlock.gif            
char[] chars = new char[n];
InBlock.gif            
for (int i = 0; i < n; i++)
ExpandedSubBlockStart.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.gif        
{
InBlock.gif            
switch (index)
ExpandedSubBlockStart.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.gif                    
{
InBlock.gif                        gen.Emit(OpCodes.Ldarg_S, index);
ExpandedSubBlockEnd.gif                    }

InBlock.gif                    
else
ExpandedSubBlockStart.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.gif        
{
InBlock.gif            
if (typeof(T).IsInterface)
ExpandedSubBlockStart.gif            
{
InBlock.gif                Type delegateType 
= GM.GetType();
InBlock.gif                
if (delegateType.IsGenericType)
ExpandedSubBlockStart.gif                
{
InBlock.gif                    
if (typeof(MulticastDelegate).IsAssignableFrom(delegateType.GetGenericTypeDefinition()))
ExpandedSubBlockStart.gif                    
{
InBlock.gif                        Type[] genericTypes 
= delegateType.GetGenericArguments();
InBlock.gif                        
if (genericTypes.Length == 1)
ExpandedSubBlockStart.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.gif
                            ConstructorBuilder ctor = builder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { delegateType });
InBlock.gif                            ILGenerator ctorGen 
= ctor.GetILGenerator();
InBlock.gif                            ctorGen.Emit(OpCodes.Ldarg_0);
ExpandedSubBlockStart.gif                            ctorGen.Emit(OpCodes.Call, 
typeof(object).GetConstructor(new Type[] { }));
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.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.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.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.gif                                
{
InBlock.gif                                    LoadArg(methodGen, i 
+ 1);
InBlock.gif                                    
if (!pi.ParameterType.IsAssignableFrom(argTypes[i]))
ExpandedSubBlockStart.gif                                    
{
InBlock.gif                                        
if (argTypes[i].IsClass)
ExpandedSubBlockStart.gif                                        
{
InBlock.gif                                            methodGen.Emit(OpCodes.Castclass, pi.ParameterType);
ExpandedSubBlockEnd.gif                                        }

InBlock.gif                                        
else
ExpandedSubBlockStart.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.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.gif                            ConstructorInfo ci 
= target.GetConstructor(new Type[] { delegateType });
ExpandedSubBlockStart.gif                            
return (T) ci.Invoke(new object[] { GM });
ExpandedSubBlockEnd.gif                        }

ExpandedSubBlockEnd.gif                    }

ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

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

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif