Reflection 发电机。这个代码怎么了

Reflection 发电机。这个代码怎么了,reflection,dynamic,ilgenerator,Reflection,Dynamic,Ilgenerator,我正在尝试构建一个动态属性访问器。想要的东西,就像真的很快接近调用实际属性。不要走反射路线,因为它很慢。所以我选择使用DynamicsSembly并使用ILGenerator注入IL。下面是与ILGenerator相关的代码,似乎可以正常工作 Label nulllabel = getIL.DefineLabel(); Label returnlabel = getIL.DefineLabel(); //_type = targetGetMetho

我正在尝试构建一个动态属性访问器。想要的东西,就像真的很快接近调用实际属性。不要走反射路线,因为它很慢。所以我选择使用DynamicsSembly并使用ILGenerator注入IL。下面是与ILGenerator相关的代码,似乎可以正常工作

        Label nulllabel = getIL.DefineLabel();
        Label returnlabel = getIL.DefineLabel();
        //_type = targetGetMethod.ReturnType;
        if (methods.Count > 0)
        {
            getIL.DeclareLocal(typeof(object));
            getIL.DeclareLocal(typeof(bool));

            getIL.Emit(OpCodes.Ldarg_1); //Load the first argument

            //(target object)

            //Cast to the source type

            getIL.Emit(OpCodes.Castclass, this.mTargetType);
            //Get the property value

            foreach (var methodInfo in methods)
            {
                getIL.EmitCall(OpCodes.Call, methodInfo, null);

                if (methodInfo.ReturnType.IsValueType)
                {
                    getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
                    //Box if necessary
                }
            }

            getIL.Emit(OpCodes.Stloc_0); //Store it
            getIL.Emit(OpCodes.Br_S,returnlabel);

            getIL.MarkLabel(nulllabel);
            getIL.Emit(OpCodes.Ldnull);
            getIL.Emit(OpCodes.Stloc_0);

            getIL.MarkLabel(returnlabel);
            getIL.Emit(OpCodes.Ldloc_0);
        }
        else
        {
            getIL.ThrowException(typeof(MissingMethodException));
        }
        getIL.Emit(OpCodes.Ret);
因此,上面得到的第一个参数是包含属性的对象。“方法”集合包含嵌套属性(如果有)。对于每个属性,我使用EmitCall将值放入堆栈,然后尝试将其装箱。这很有魅力

唯一的问题是,如果您有一个类似Order.Instrument.Symbol.Name的属性,并假设Instrument对象为null。然后,代码将抛出null对象异常

这就是我所做的,我引入了一个空检查

            foreach (var methodInfo in methods)
            {
                getIL.EmitCall(OpCodes.Call, methodInfo, null);

                getIL.Emit(OpCodes.Stloc_0);
                getIL.Emit(OpCodes.Ldloc_0);

                getIL.Emit(OpCodes.Ldnull);
                getIL.Emit(OpCodes.Ceq);
                getIL.Emit(OpCodes.Stloc_1);
                getIL.Emit(OpCodes.Ldloc_1);
                getIL.Emit(OpCodes.Brtrue_S, nulllabel);
                getIL.Emit(OpCodes.Ldloc_0);

                if (methodInfo.ReturnType.IsValueType)
                {
                    getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
                    //Box if necessary
                }
            }
现在这段代码中断,表示对象/内存已损坏等。那么这段代码到底有什么问题呢。我是不是遗漏了什么


提前感谢。

之前,如果您有连续的属性p返回字符串,然后Q返回int,您将得到如下结果:

...   
call P // returns string
call Q // requires a string on the stack, returns an int
box
...
...
call P // returns string
store  // stores to object
...    // load, compare to null, etc.
load   // loads an *object*
call Q // requires a *string* on the stack
store  // stores to object *without boxing*
...
现在你有了这样的东西:

...   
call P // returns string
call Q // requires a string on the stack, returns an int
box
...
...
call P // returns string
store  // stores to object
...    // load, compare to null, etc.
load   // loads an *object*
call Q // requires a *string* on the stack
store  // stores to object *without boxing*
...
因此,我看到两个明显的问题:

您调用方法的方式是,只知道目标是一个对象,而不是具有该方法的特定类型。 在将值类型存储到对象的本地类型之前,不能对其进行装箱。 这些问题可以通过稍微修改逻辑来解决。您还可以清理一些其他小细节:

使用beq,而不是ceq后跟brtrue。 在Stloc_1后面加上Ldloc_1,而不是仅仅使用堆栈上的值,这是没有意义的,因为该本地值不在其他任何地方使用。 结合这些变化,以下是我要做的:

Type finalType = null;

foreach (var methodInfo in methods)
{
    finalType = methodInfo.ReturnType;

    getIL.EmitCall(OpCodes.Call, methodInfo, null);
    if (!finalType.IsValueType)
    {
        getIL.Emit(OpCodes.Dup);
        getIL.Emit(OpCodes.Ldnull);
        getIL.Emit(OpCodes.Beq_S, nulllabel);
    }
}

if (finalType.IsValueType)
{
    getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
    //Box if necessary
}

getIL.Emit(OpCodes.Br_S, returnLabel);

getIL.MarkLabel(nulllabel);
getIL.Emit(OpCodes.Pop);    
getIL.Emit(OpCodes.Ldnull);

getIL.MarkLabel(returnlabel);

请注意,我们可以去掉这两个局部变量,因为我们现在只需复制堆栈上的顶部值,然后再与null进行比较。

以前,如果您有连续的属性p返回字符串,然后Q返回int,您将得到如下结果:

...   
call P // returns string
call Q // requires a string on the stack, returns an int
box
...
...
call P // returns string
store  // stores to object
...    // load, compare to null, etc.
load   // loads an *object*
call Q // requires a *string* on the stack
store  // stores to object *without boxing*
...
现在你有了这样的东西:

...   
call P // returns string
call Q // requires a string on the stack, returns an int
box
...
...
call P // returns string
store  // stores to object
...    // load, compare to null, etc.
load   // loads an *object*
call Q // requires a *string* on the stack
store  // stores to object *without boxing*
...
因此,我看到两个明显的问题:

您调用方法的方式是,只知道目标是一个对象,而不是具有该方法的特定类型。 在将值类型存储到对象的本地类型之前,不能对其进行装箱。 这些问题可以通过稍微修改逻辑来解决。您还可以清理一些其他小细节:

使用beq,而不是ceq后跟brtrue。 在Stloc_1后面加上Ldloc_1,而不是仅仅使用堆栈上的值,这是没有意义的,因为该本地值不在其他任何地方使用。 结合这些变化,以下是我要做的:

Type finalType = null;

foreach (var methodInfo in methods)
{
    finalType = methodInfo.ReturnType;

    getIL.EmitCall(OpCodes.Call, methodInfo, null);
    if (!finalType.IsValueType)
    {
        getIL.Emit(OpCodes.Dup);
        getIL.Emit(OpCodes.Ldnull);
        getIL.Emit(OpCodes.Beq_S, nulllabel);
    }
}

if (finalType.IsValueType)
{
    getIL.Emit(OpCodes.Box, methodInfo.ReturnType);
    //Box if necessary
}

getIL.Emit(OpCodes.Br_S, returnLabel);

getIL.MarkLabel(nulllabel);
getIL.Emit(OpCodes.Pop);    
getIL.Emit(OpCodes.Ldnull);

getIL.MarkLabel(returnlabel);
请注意,我们可以去掉这两个局部变量,因为我们现在只需在与null进行比较之前复制堆栈上的top值