Разрешено ли присваивать по ссылке в поле с помощью IL? - PullRequest
4 голосов
/ 06 декабря 2010

Насколько я знаю, нет способа взаимодействия с ссылочными типами в деревьях выражений. (например, ничего не генерирует 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 в его нынешнем виде. Таким образом, когда делегат выполняет мутацию в поле значения, изменения передаются. Это означает, что мне не нужно иметь переменную для хранения возвращаемого значения, и мне не нужно выполнять обновление эталонного значения.

В качестве альтернативы, если есть способ выполнить семантику присваивания по ссылке с деревьями выражений, я, конечно, все уши.

1 Ответ

0 голосов
/ 13 марта 2013

Не уверен, что я действительно понимаю, но, возможно, это решение:

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().

...