代码之家  ›  专栏  ›  技术社区  ›  Lasse V. Karlsen

constructrinfo.invoke的dynamicMethod,我需要考虑什么?

  •  3
  • Lasse V. Karlsen  · 技术社区  · 15 年前

    我的问题是:

    如果我要建立一个 DynamicMethod 对象,对应于 ConstructorInfo.Invoke 调用,为了处理所有(或大多数)类型的参数,我需要实现什么类型的IL,当我可以保证在调用之前传入正确的类型和参数数量时?


    背景

    我正在进行我的IOC容器的第三次迭代,目前正在做一些分析,以确定是否有任何领域可以轻松地减少大量时间的使用。

    我注意到的一件事是,当解析为具体类型时,最终我得到了一个调用构造函数的结果,使用 constructorInfo.invoke ,传入一组我已经计算出的参数。

    我注意到invoke方法有相当多的开销,我想知道这其中的大部分是否只是我所做的相同检查的不同实现。

    例如,由于构造函数与我所拥有的代码相匹配,要为我传入的预定义参数名称、类型和值找到匹配的构造函数,这个特定的invoke调用不可能以它应该能够处理的内容(如正确的参数数目,以正确的顺序,正确的类型)结束,并具有适当的值。

    在执行包含对我的resolve方法的一百万个调用的分析会话时,然后将其替换为 动态方法 实现模拟调用,分析时间如下:

    • constructorinfo.invoke:1973ms
    • 动态方法:93ms

    这大约占这个分析应用程序总运行时间的20%。换句话说,通过将constructrinfo.invoke调用替换为执行相同操作的dynamicMethod,我可以在处理基本的工厂范围的服务(即,所有解析调用最终都是一个构造函数调用)时减少20%的运行时。

    我认为这是相当可观的,并且需要更仔细地看看在这种情况下为构造函数构建一个稳定的动态方法生成器需要多少工作。

    所以,动态方法将接受一个对象数组,并返回被构造的对象,我已经知道有问题的constructrinfo对象。

    因此,看起来动态方法将由以下IL组成:

    l001:    ldarg.0      ; the object array containing the arguments
    l002:    ldc.i4.0     ; the index of the first argument
    l003:    ldelem.ref   ; get the value of the first argument
    l004:    castclass T  ; cast to the right type of argument (only if not "Object")
    (repeat l001-l004 for all parameters, l004 only for non-Object types,
     varying l002 constant from 0 and up for each index)
    l005:    newobj ci    ; call the constructor
    l006:    ret
    

    还有什么需要考虑的吗?

    请注意,我知道在“简化访问模式”下运行应用程序时,创建动态方法可能不可用(有时大脑不会放弃这些术语),但在这种情况下,我可以很容易地检测到这一点,并像以前一样调用原始的构造函数,同时使用开销和所有开销。

    2 回复  |  直到 15 年前
        1
  •  1
  •   Pent Ploompuu    15 年前

    对于值类型,步骤l004应为 l004: unbox.any T .

    找出您需要生成的正确IL的最简单方法是查看C编译器使用一些测试代码生成的内容。

    static void Test(object[] args)
    {
      TestTarget((string)args[0], (int)args[1], (DateTime?)args[2]);
    }
    
    static void TestTarget(string s, int i, DateTime? dt){}
    

    编译到:

    L_0000: ldarg.0 
    L_0001: ldc.i4.0 
    L_0002: ldelem.ref 
    L_0003: castclass string
    L_0008: ldarg.0 
    L_0009: ldc.i4.1 
    L_000a: ldelem.ref 
    L_000b: unbox.any int32
    L_0010: ldarg.0 
    L_0011: ldc.i4.2 
    L_0012: ldelem.ref 
    L_0013: unbox.any [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>
    L_0018: call void Program::TestTarget(string, int32, valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>)
    L_001d: ret 
    
        2
  •  0
  •   Morten Mertner    15 年前

    有一些库可以使反射更容易(更快)工作。例如, Fasterflect 可以为调用任何构造函数生成IL—您只需将要在构造函数上使用的参数传递给它。

    // note: class must have constructor with (int,string,string) signature
    object obj = someType.CreateInstance( new { id=1, name="jens", foo="bar" } );
    

    如果没有一组与构造函数完全匹配的参数,则库还可以探测到要调用的适当构造函数。

    // try to map id, name and foo to constructor parameters
    // allows changing the order and permit fallback to setting fields/properties
    // e.g. might result in call to ctor(string,string) and set field "id"
    object obj = someType.TryCreateInstance( new { id=1, name="jens", foo="bar" } );
    

    免责声明:我作为一个贡献者参与了这个项目。