2012-09-07 19 views
10

Quiero definir una expresión Lambda con un parámetro out. ¿Es posible hacer lo?¿Es posible tener un out ParameterExpression?

A continuación se muestran los fragmentos de código de una aplicación de consola C# .Net 4.0 que probé.

Como se puede ver en Procedure25 puedo usar expresiones lambda para definir un delegado que tiene un parámetro de salida, sin embargo, cuando quiero utilizar expresiones LINQ a hacer lo mismo, el código de procedimiento 24 falla con:

System.ArgumentException fue no controlada Mensaje = ParameterExpression del tipo 'System.Boolean' no se puede usar para el parámetro delegado de tipo 'System.Boolean &' Fuente = System.Core

sé que podría utilizar una clase de entrada obj ect con un miembro bool y le devuelve el valor a la persona que llama de esa manera, pero tenía curiosidad si de alguna manera podía definir los parámetros.

Gracias

static void Main(string[] args) 
{ 
    Procedure25(); 
    Procedure24(); 
    Console.WriteLine("Done!"); 
    Console.ReadKey(); 
} 

private delegate int Evaluate(string value, out bool usesVars); 

private static void Procedure24() 
{ 

    // This fails to compile: 
    //Expression<Evaluate> x = (string val, out bool usesSimVals) => 
    //{ 
    // usesSimVals = true; 
    // Console.WriteLine(val); 
    // return 1; 
    //}; 


    ParameterExpression valueParameter = Expression.Parameter(typeof (string)); 
    MethodCallExpression methodCall = Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }), valueParameter); 

    bool usesVars; 
    ParameterExpression usesVarsParameter = Expression.Parameter(typeof (bool), "out usesVars"); 


    Expression.Lambda<Evaluate>(methodCall, valueParameter, usesVarsParameter).Compile()("test", out usesVars); 
    Console.WriteLine(usesVars); 

} 

private static void Procedure25() 
{ 
    Evaluate x = (string value, out bool vars) => { vars = true; 
    Console.WriteLine(value); 
                return 1; 
    }; 

    bool usesVars; 
    x("test", out usesVars); 
} 

EDIT:

Ani, impresionante, gracias. Entonces, la clave era llamar a MakeByRefType en el tipo de parámetro.

Para el registro aquí es un fragmento de código que funciona basado en la sugerencia de Ani:

private static void Procedure24() 
{ 
    ParameterExpression valueParameter = Expression.Parameter(typeof (string)); 
    MethodCallExpression methodCall = Expression.Call(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(object) }), valueParameter); 

    bool usesVars; 
    ParameterExpression usesVarsParameter = Expression.Parameter(typeof (bool).MakeByRefType(), "out usesVars"); 

    Expression block = Expression.Block(methodCall, Expression.Assign(usesVarsParameter, Expression.Constant(true)), Expression.Constant(1)); 
    int result = Expression.Lambda<Evaluate>(block, valueParameter, usesVarsParameter).Compile()("test", out usesVars); 
    Console.WriteLine("Result={0}, usesVars={1}", result, usesVars); 

} 

Respuesta

7

Es necesario Type.MakeByRefType:

var usesVarsParameter = Expression.Parameter(typeof(bool).MakeByRefType(), "usesVars"); 

en cuenta que su ejemplo de código tiene un problema adicional: su expresión: body no es correcto; no devuelve un valor cuando debería devolver un int para satisfacer el tipo de devolución del tipo de delegado.

Aquí está una manera que usted puede arreglar eso (como su ejemplo lambda):

var body = Expression.Block(methodCall, Expression.Constant(1)); 

Expression.Lambda<Evaluate>(body, valueParameter, usesVarsParameter) 
      .Compile()("test", out usesVars); 

También tenga en cuenta que no se está asignando el parámetro out dentro de la expresión. Expression.Lambda te permite salirte con la tuya, lo que no esperaba, pero bueno, ¡el BCL no tiene que seguir las mismas reglas que C#!

Cuestiones relacionadas