lik*_*her 1 reflection dynamic ilgenerator
我正在尝试构建一个动态的Property Accessor。想要像真正调用Property一样快的东西。不想走反射路线,因为它非常慢。所以我选择使用DynamicAssembly并使用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。然后,代码将引发空对象异常。
所以我做了这个,我引入了空检查
            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*
...
所以我看到两个明显的问题:
这些可以通过稍微修改逻辑来解决。您还可以清除其他一些小细节:
ceq紧随其后brtrue,只是使用beq。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进行比较即可。