c# - sintaxis - No se puede usar el parámetro ref o out en expresiones lambda
metodos anonimos c# (4)
¿Por qué no puedes usar un parámetro ref o out en una expresión lambda?
Me encontré con el error hoy y encontré una solución, pero todavía tenía curiosidad de por qué este es un error en tiempo de compilación.
CS1628 : No se puede utilizar en el parámetro ref o out ''parameter'' dentro de un método anónimo, expresión lambda o expresión de consulta
Aquí hay un ejemplo simple:
private void Foo()
{
int value;
Bar(out value);
}
private void Bar(out int value)
{
value = 3;
int[] array = { 1, 2, 3, 4, 5 };
int newValue = array.Where(a => a == value).First();
}
Bajo el capó, el método anónimo se implementa al alzar las variables capturadas (que es de lo que se trata su cuerpo de preguntas) y almacenarlas como campos de una clase generada por el compilador. No hay forma de almacenar un parámetro ref
o out
como un campo. Eric Lippert lo discutió en una entrada de blog . Tenga en cuenta que existe una diferencia entre las variables capturadas y los parámetros lambda. Puede tener "parámetros formales" como los siguientes ya que no son variables capturadas:
delegate void TestDelegate (out int x);
static void Main(string[] args)
{
TestDelegate testDel = (out int x) => { x = 10; };
int p;
testDel(out p);
Console.WriteLine(p);
}
Como este es uno de los mejores resultados para "C # lambda ref" en Google; Siento que necesito expandir las respuestas anteriores. La sintaxis de delegado anónimo más antigua (C # 2.0) funciona y admite firmas más complejas (así como cierres). Por lo menos, los delegados de Lambda y anónimos han compartido la implementación percibida en el backend del compilador (si no son idénticos), y lo más importante, admiten los cierres.
Lo que estaba tratando de hacer cuando hice la búsqueda, para demostrar la sintaxis:
public static ScanOperation<TToken> CreateScanOperation(
PrattTokenDefinition<TNode, TToken, TParser, TSelf> tokenDefinition)
{
var oldScanOperation = tokenDefinition.ScanOperation; // Closures still work.
return delegate(string text, ref int position, ref PositionInformation currentPosition)
{
var token = oldScanOperation(text, ref position, ref currentPosition);
if (token == null)
return null;
if (tokenDefinition.LeftDenotation != null)
token._led = tokenDefinition.LeftDenotation(token);
if (tokenDefinition.NullDenotation != null)
token._nud = tokenDefinition.NullDenotation(token);
token.Identifier = tokenDefinition.Identifier;
token.LeftBindingPower = tokenDefinition.LeftBindingPower;
token.OnInitialize();
return token;
};
}
Solo tenga en cuenta que Lambdas es procesal y matemáticamente más seguro (debido a la promoción del valor de referencia mencionada anteriormente): puede abrir una lata de gusanos. Piense con cuidado cuando use esta sintaxis.
Lambdas tiene la apariencia de cambiar la vida útil de las variables que capturan. Por ejemplo, la siguiente expresión lambda hace que el parámetro p1 viva más tiempo que el marco de método actual, ya que se puede acceder a su valor después de que el marco del método ya no esté en la pila
Func<int> Example(int p1) {
return () => p1;
}
Otra propiedad de las variables capturadas es que los cambios en la variable también son visibles fuera de la expresión lambda. Por ejemplo, las siguientes impresiones 42
void Example2(int p1) {
Action del = () => { p1 = 42; }
del();
Console.WriteLine(p1);
}
Estas dos propiedades producen un cierto conjunto de efectos que sobrevuelan un parámetro ref de las siguientes maneras
- Los parámetros ref pueden tener una vida útil fija. Considere pasar una variable local como un parámetro de referencia a una función.
- Los efectos secundarios en el lambda tendrían que ser visibles en el propio parámetro ref. Tanto dentro del método como en la persona que llama.
Estas son propiedades algo incompatibles y son una de las razones por las que no se permiten en expresiones lambda.
Puede, pero debe definir explícitamente todos los tipos para
(a, b, c, ref d) => {...}
No es válido, sin embargo
(int a, int b, int c, ref int d) => {...}
Es válida