首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ILGenerator。这段代码有什么问题?

ILGenerator。这段代码有什么问题?
EN

Stack Overflow用户
提问于 2011-09-21 23:56:53
回答 1查看 1.7K关注 0票数 1

我正在尝试构建一个动态属性访问器。我想要一些非常快的东西,就像调用actually属性一样快。我不想走反射路线,因为它非常慢。因此,我选择使用DynamicAssembly,并使用ILGenerator注入IL。下面是与ILGenerator相关的代码,看起来还不错

代码语言:javascript
复制
        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);

所以在上面获取第一个参数,它是包含属性的对象。methods集合包含嵌套属性(如果有)。对于每个属性,我使用EmitCall,它将值放到堆栈上,然后尝试对其进行装箱。这就像一个护身符。

唯一的问题是,如果你有一个像Order.Instrument.Symbol.Name这样的属性,并假设仪器对象为空。然后,代码将抛出空对象异常。

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

代码语言:javascript
复制
            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
                }
            }

现在这段代码中断了,说对象/内存被破坏了等等。那么这段代码到底出了什么问题。我是不是漏掉了什么。

提前谢谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-09-27 05:38:26

以前,如果您有连续的属性P返回string,然后Q返回int,您将得到如下所示:

代码语言:javascript
复制
...   
call P // returns string
call Q // requires a string on the stack, returns an int
box
...

现在你有了类似这样的东西:

代码语言:javascript
复制
...
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*
...

因此,我看到了两个明显的问题:

  1. 您调用方法的方式使目标只知道是一个对象,而不是具有该方法的特定类型。
  2. 在将值类型存储到object类型的本地之前,您不能对其进行装箱。

这些问题可以通过稍微修改一下您的逻辑来解决。您还可以清理其他一些次要的细节:

使用beq.

  • There's,而不是先使用

  • ,再使用ceq,再使用brtrue,这样做没有意义,后面跟着Stloc_1,而不是只使用堆栈上的值,因为其他任何地方都不会使用该本地值。

结合这些变化,以下是我要做的:

代码语言:javascript
复制
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值。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7502865

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档