惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
H
Help Net Security
博客园_首页
P
Privacy International News Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tenable Blog
Latest news
Latest news
D
Darknet – Hacking Tools, Hacker News & Cyber Security
爱范儿
爱范儿
Cyberwarzone
Cyberwarzone
P
Palo Alto Networks Blog
月光博客
月光博客
有赞技术团队
有赞技术团队
Know Your Adversary
Know Your Adversary
博客园 - 叶小钗
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
Schneier on Security
A
About on SuperTechFans
F
Full Disclosure
The Cloudflare Blog
T
The Exploit Database - CXSecurity.com
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
Microsoft Security Blog
Microsoft Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
News and Events Feed by Topic
www.infosecurity-magazine.com
www.infosecurity-magazine.com
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Webroot Blog
Webroot Blog
量子位
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
WordPress大学
WordPress大学
Last Week in AI
Last Week in AI
美团技术团队
Help Net Security
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
GbyAI
GbyAI
云风的 BLOG
云风的 BLOG
Y
Y Combinator Blog
博客园 - 司徒正美
N
Netflix TechBlog - Medium
S
Security @ Cisco Blogs
B
Blog
P
Privacy & Cybersecurity Law Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Hacker News - Newest:
Hacker News - Newest: "LLM"
V
Visual Studio Blog
NISL@THU
NISL@THU

博客园 - 双鱼座

永中科技倒闭的另一面 ASP.NET MVC + ADO.NET EF 项目实战(三):引入jQuery 虎年刚开头,给大家拜个年... 每一个社区成员,用你的努力为微软平台的开发技术添上一把柴! POCO真那么重要么? 也说 ASP.NET MVC的 Script 管理 IT风云录(开篇词):我们从David Cutler学到什么? ASP.NET MVC的RAD之路(一) ASP.NET MVC + ADO.NET EF 项目实战(二):设计过程与设计工具 体验ADO.NET Entity Framework的继承 MSDN,微软怎么会这样啊? ASP.NET MVC + ADO.NET EF 项目实战(一):应用程序布局设计 用asp.net mvc写一个后台文件上传 ORM漫谈 对Delphi 7/Delphi 2007的Windows服务类库的一个小改进 案例分析:面向对象得失论 闲话时间调度算法 2007体检结果出来了 在ASP.NET应用程序中捕捉身份验证状态的变化
以非泛型方式调用泛型方法
双鱼座 · 2007-02-07 · via 博客园 - 双鱼座

通过泛型方法定义具有特定类型意义的方法是常用的手段。但在某些特定情况下,例如在一些通用的框架中,直到运行时才能确定泛型类型参数,就必须通过非泛型方式来调用泛型方法。
假定有这样一个方法:

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 成员
}

然后就可以非常简单地使用已有的泛型方法来获得一个非泛型接口实现了:

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);
                    
break;
                
case 2:
                    gen.Emit(OpCodes.Ldarg_2);
                    
break;
                
case 3:
                    gen.Emit(OpCodes.Ldarg_3);
                    
break;
                
default:
                    
if (index < 128)
                    
{
                        gen.Emit(OpCodes.Ldarg_S, index);
                    }

                    
else
                    
{
                        gen.Emit(OpCodes.Ldarg, index);
                    }

                    
break;
            }

        }


        
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,
#if SAVE
 AssemblyBuilderAccess.RunAndSave
#else
 AssemblyBuilderAccess.Run
#endif
);
                            ModuleBuilder module 
= assembly.DefineDynamicModule(GetRandomString(8)
#if SAVE
, theFilename
#endif
);
                            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_0);
                            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();
#if SAVE
                            assembly.Save(theFilename);
#endif
                            ConstructorInfo ci 
= target.GetConstructor(new Type[] { delegateType });
                            
return (T) ci.Invoke(new object[] { GM });
                        }

                    }

                }

            }

            
return default(T);
        }

    }

}

结论:
以下是测试代码:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace GenericMethodTest
{
    
// 为泛型方法定义的委托
    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;
            watch.Reset();
            watch.Start();
            GM
<int> gm = new GM<int>(Program.Add);
            
for (int i = 0; i < 1000000; i++)
            
{
                gm(i, list);
            }

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

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

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

            watch.Stop();
            
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 成员
    }

}

测试结果:

方案 耗时 比对 其他优点
直接调用 18 1 不通用
泛型委托包装 43 2.39 不通用
反射 16538 918.78 通用,不需额外定义
非泛型接口包装 60 3.33 通用,需要额外定义并实现
动态生成的非泛型接口包装 72 4 通用,需要额外定义