2011-02-07 11 views
15

Estoy tratando de crear un método utilizando un árbol de expresiones que devuelve un objeto, pero no puedo encontrar la forma de especificar el objeto que se devolverá. Intenté leer this, pero el valor de retorno no parece estar especificado en ninguna parte.¿Cómo especifico el objeto para devolver desde un método de árbol de expresión?

Tengo todas las asignaciones & cosas hacia abajo, pero ¿cómo especifico el objeto para devolver desde un método creado usando árboles de expresiones?

EDIT: estos son los árboles de expresión 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; 
} 
+0

Dado que esto realmente marca la diferencia, ¿son estos árboles de expresión v3 o v4? Además, ¿puedes mostrar un ejemplo del código que intentas representar con el árbol? –

+0

v4 árboles de expresión. Editado para agregar código. – thecoop

Respuesta

18

Al parecer, un return es una GotoExpression que se puede crear con el método Expression.Return fábrica. 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 del objetivo de etiqueta y la expresión goto deben coincidir. Debido a que el objetivo de etiqueta tiene un tipo, la expresión de etiqueta debe tener un valor predeterminado.

+1

¿A dónde apunta el argumento 'etiqueta '? – thecoop

+2

@thecoop: Tuve que cambiar algunas otras cosas, pero lo hice funcionar. ¡Demonios esto seguro es un montón de problemas para trabajar! –

+2

Ay, eso funciona. ¿Alguna idea de por qué es tan complicada? – thecoop

11

He encontrado un par de fuentes que implican (un caso) o declaran (otro caso) que devolver un valor de una expresión se puede hacer simplemente concluyendo el bloque con la expresión de parámetro que corresponde al valor en cuestión , ya que la última expresión valorada de un bloque se convierte en su valor de retorno. Según se informa, la fábrica Expression.Return existe para casos más complicados donde uno regresa desde la mitad de un bloque de código.

En otras palabras, si la última expresión dentro de su bloque fuera simplemente objExpression, eso debería ser suficiente. Creo que si deconstruyes todo ese negocio con el método Return y la etiqueta, lo que realmente ocurre es que objExpression se entrega básicamente en la etiqueta (al final del bloque) y se deja allí, de forma muy similar a como lo harías si eliminaras returnExpression y returnLabel y simplemente concluyó con objExpression.

Lamentablemente, no estoy en condiciones de probarlo yo mismo.

+4

Verificado en .NET 4.5 – bugventure

+0

funciona para mí en .NET 4. incluso simplemente 'Expression.Constant (null)' al final de la expresión de bloque para devolver nulo. –

22

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

Asumiendo que su estructura es la siguiente:

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

A continuación, el 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 que funciona:

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

Vale la pena mencionar este ejemplo funciona si StructType es una estructura. Si es una clase, simplemente llama al constructor e inicializa structVar en primer lugar en BlockExpression.

Cuestiones relacionadas