Как указать объект для возврата из метода дерева выражений?

Я пытаюсь создать метод, используя дерево выражений, которое возвращает объект, но я не могу понять, как фактически указать возвращаемый объект. Я пробовал читать этот, но возвращаемое значение на самом деле не похоже нигде.

У меня есть все назначения и прочее, но как указать объект для возврата из метода, созданного с помощью деревьев выражений?

EDIT: это деревья выражений v4, а метод, который я пытаюсь создать, делает примерно так:

private object ReadStruct(BinaryReader reader) {
    StructType obj = new StructType();
    obj.Field1 = reader.ReadSomething();
    obj.Field2 = reader.ReadSomething();
    //...more...
    return obj;
}

Ответ 1

По-видимому, return является GotoExpression, который вы можете создать с помощью Expression.Return factory. Вам нужно создать ярлык в конце, чтобы перейти к нему. Что-то вроде этого:

// an expression representing the obj variable
ParameterExpression objExpression = ...;

LabelTarget returnTarget = Expression.Label(typeof(StructType));

GotoExpression returnExpression = Expression.Return(returnTarget, 
    objExpression, typeof(StructType));

LabelExpression returnLabel = Expression.Label(returnTarget, defaultValue);

BlockExpression block = Expression.Block(
    /* ... variables, all the other lines and... */,
    returnExpression,
    returnLabel);

Типы целевой метки и выражение goto должны совпадать. Поскольку цель метки имеет тип, выражение метки должно иметь значение по умолчанию.

Ответ 2

Существует гораздо более простой способ сделать это в случаях, возвращающих существующий параметр или переменную. Возвращаемое значение становится последним в блочном выражении. Вы можете включить ParameterExpression снова в конце, чтобы вернуть его.

Предполагая, что ваша структура выглядит так:

public struct StructType
{
    public byte Field1;
    public short Field2;
}

Тогда ваш код будет выглядеть так:

var readerType = typeof(BinaryReader);
var structType = typeof(StructType);
var readerParam = Expression.Parameter(readerType);
var structVar = Expression.Variable(structType);

var expressions = new List<Expression>();

expressions.Add(
    Expression.Assign(
        Expression.MakeMemberAccess(structVar, structType.GetField("Field1")),
        Expression.Call(readerParam, readerType.GetMethod("ReadByte"))
        )
    );

expressions.Add(
    Expression.Assign(
        Expression.MakeMemberAccess(structVar, structType.GetField("Field2")),
        Expression.Call(readerParam, readerType.GetMethod("ReadInt16"))
        )
    );

expressions.Add(structVar); //This is the key. This will be the return value.

var ReadStruct = Expression.Lambda<Func<BinaryReader, StructType>>(
    Expression.Block(new[] {structVar}, expressions),
    readerParam).Compile();

Проверьте, что он работает:

var stream = new MemoryStream(new byte[] {0x57, 0x46, 0x07});
var reader = new BinaryReader(stream);
var struct1 = ReadStruct(reader);

Стоит отметить, что этот пример работает, если StructType является структурой. Если это класс, вы просто вызываете конструктор и сначала инициализируете structVar в BlockExpression.

Ответ 3

Я нашел несколько источников, которые подразумевают (один случай) или фактическое состояние (другое дело), ​​возвращающее значение из выражения, просто можно сделать, заключая блок с выражением параметра, который соответствует рассматриваемому значению, поскольку последнее значащее выражение из блока становится его возвращаемым значением. Как сообщается, Expression.Return factory существует для более сложных случаев, когда один возвращается из середины блока кода.

Другими словами, если последнее выражение внутри вашего блока было просто objExpression, этого должно быть достаточно. Я думаю, что если вы деконструируете все это дело с помощью метода Return и метки, то, что на самом деле происходит, является objExpression в основном доставлено на метку (в конце блока) и остается там, что бы произошло, если вы исключили returnExpression и returnLabel и просто заключен с objExpression.

К сожалению, я не в состоянии фактически проверить это сам.