¿Es posible tener una expresión de parámetro fuera?

.net .net-4.0 c# expression-trees linq

Pregunta

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

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

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

System.ArgumentException no se manejó Mensaje = ParameterExpression de tipo 'System.Boolean' no se puede usar para el parámetro delegado de tipo 'System.Boolean &' Source = System.Core

Sé que podría usar un objeto de clase de entrada con un miembro bool y devolverle el valor al llamante de esa manera, pero tenía curiosidad de poder definir de alguna manera 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);
}

EDITAR:

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

Para el registro aquí hay un fragmento de código que funciona según 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 aceptada

Necesitas Type.MakeByRefType :

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

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

Aquí hay una forma en que puede solucionarlo (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 está asignando el parámetro out dentro de la expresión. Expression.Lambda Lambda te está dejando escapar, lo cual no esperaba, pero bueno, ¡el BCL no tiene que seguir las mismas reglas que C #!



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