使用IL将ref分配给一个字段是否合法?

时间:2010-12-06 16:12:30

标签: c# clr reflection.emit

据我所知,无法与Expression树中的引用类型进行交互。 (例如,没有任何内容会发出stind.*ldind.*操作码。 我正在研究一些改写器来解决这个烦恼。因为我正在构建一个新的类型,它将方法体替换为委托调用(以解决CompileToMethod只能执行无法与新成员交互的静态方法的事实)。对于by-ref和out参数,我想我会用StrongBox<T>替换它们的用法。

因此,如果我遇到一个方法,其签名看起来像这样::

public class SomeClass
{
    public virtual bool SomeMethod(string arg1,ref int arg2)
    {
    }
}

我生成的覆盖,callbase方法和委托字段将如下所示::

public class SomeClass<1> : SomeClass
{
     private static bool SomeMethod<0>(
             SomeClass target,string arg1,StrongBox<int> arg2)
     {
        return call target.SomeMethod(arg1,ref arg2.Value)
     }

     private Func<SomeClass,string,StrongBox<int>,bool> <0>SomeMethod;

    public override bool SomeMethod(string arg1,ref int arg2)
    {
        StrongBox<int> box = new StrongBox<int>();
        box.Value = arg2;
        bool retVal = <0>SomeMethod.Invoke(this,arg1,box);
        arg2 = box.Value;
        return retVal;
    }
}

然而,这是执行此转换的相当多的代码,对于每个参数,它引入了很多复杂性。当我执行box.Value = arg2的设置时会更容易,如果我可以执行类似&box.Value = &arg2的操作,将其地址分配给arg2的地址。这样,当委托对值字段执行变异时,将转发更改。这样做意味着我不需要有一个变量来保存返回值,我不需要执行参考值更新。

或者,如果有一种方法可以使用表达式树执行ref-by-ref语义,我当然都是耳朵。

1 个答案:

答案 0 :(得分:0)

不确定我是否真的理解,但也许这是一个解决方案:

class Program
{

    public class SomeClass
    {
        private readonly int _n;

        public SomeClass(int n) { _n = n; }

        public virtual bool SomeMethod(string arg1, ref int arg2) {
            if (String.IsNullOrWhiteSpace(arg1)) return false;
            arg2 += arg1.Length + _n;
            return true;
        }
    }

    private delegate bool SomeDelegate(SomeClass that, string arg1, ref int arg2);

    static void Main(string[] args) {
        var instance = Expression.Parameter(typeof (SomeClass), "that");
        var arg1Param = Expression.Parameter(typeof(string), "arg1");
        var arg2Param = Expression.Parameter(typeof (int).MakeByRefType(), "arg2");
        var someMethodInfo = typeof (SomeClass).GetMethod("SomeMethod");
        var lambda = Expression.Lambda<SomeDelegate>(Expression.Call(instance, someMethodInfo, arg1Param, arg2Param), instance, arg1Param, arg2Param);
        var someDelegate =lambda.Compile();
        var myClass = new SomeClass(2);
        var arg1 = "yup";
        var arg2 = 1;
        var result = someDelegate(myClass, arg1, ref arg2);
        if(arg2 != 6) throw new Exception("Bad!");
        Console.WriteLine("works...");
    }

}

我认为重要的一点是typeof (int).MakeByRefType()