¿Cómo especifico el objeto para devolver desde un método de árbol de expresión?

.net expression-trees

Pregunta

Estoy tratando de crear un método utilizando un árbol de expresiones que devuelve un objeto, pero no puedo averiguar cómo especificar realmente el objeto para devolver. He intentado leer esto , pero el valor de retorno en realidad no parece estar especificado en ninguna parte.

Tengo todas las asignaciones y cosas pendientes, pero ¿cómo puedo especificar el objeto para devolver desde un método creado utilizando árboles de expresión?

EDITAR: estos son árboles de expresiones v4, y el método que estoy tratando de crear hace algo como esto:

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

Respuesta aceptada

Al parecer, una return es una GotoExpression que puede crear con el método de fábrica de Expression.Return . Necesitas crear una etiqueta al final para saltar a ella. Algo como esto:

// 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);

Los tipos de destino de la etiqueta y la expresión goto deben coincidir. Debido a que la etiqueta de destino tiene un tipo, la expresión de la etiqueta debe tener un valor predeterminado.


Respuesta popular

Hay una forma mucho más fácil de hacer esto en los casos que devuelven un parámetro o variable existente. La última declaración en una expresión de bloque se convierte en el valor de retorno. Puede incluir la ParameterExpression de nuevo al final para que se devuelva.

Asumiendo que tu estructura es así:

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

Entonces su código se vería así:

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

Prueba de que funciona:

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

Vale la pena mencionar que este ejemplo funciona si StructType es una estructura. Si es una clase, simplemente llame al constructor e inicialice structVar a primera hora en BlockExpression.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow