expressions expresiones delegate c# .net-3.5 delegates lambda implicit-typing

expresiones - linq lambda c#



¿Por qué no se puede asignar un método anónimo a var? (6)

Tengo el siguiente código:

Func<string, bool> comparer = delegate(string value) { return value != "0"; };

Sin embargo, lo siguiente no compila:

var comparer = delegate(string value) { return value != "0"; };

¿Por qué el compilador no puede descubrir que es una Func<string, bool> ? Toma un parámetro de cadena y devuelve un booleano. En cambio, me da el error:

No se puede asignar un método anónimo a una variable local implícitamente tipada.

Tengo una conjetura y es que si se compila la versión var , me faltaría consistencia si tuviera lo siguiente:

var comparer = delegate(string arg1, string arg2, string arg3, string arg4, string arg5) { return false; };

Lo anterior no tendría sentido ya que Func <> solo permite hasta 4 argumentos (en .NET 3.5, que es lo que estoy usando). Quizás alguien podría aclarar el problema. Gracias.


¿Cómo es eso?

var item = new { toolisn = 100, LangId = "ENG", toolPath = (Func<int, string, string>) delegate(int toolisn, string LangId) { var path = "/Content/Tool_" + toolisn + "_" + LangId + "/story.html"; return File.Exists(Server.MapPath(path)) ? "<a style=/"vertical-align:super/" href=/"" + path + "/" target=/"_blank/">execute example</a> " : ""; } }; string result = item.toolPath(item.toolisn, item.LangId);


Diferentes delegados se consideran diferentes tipos. por ejemplo, Action es diferente de MethodInvoker , y una instancia de Action no se puede asignar a una variable de tipo MethodInvoker .

Entonces, dado un delegado anónimo (o lambda) como () => {} , ¿es una Action o un MethodInvoker ? El compilador no puede decir.

De manera similar, si declaro que un tipo de delegado toma un argumento de string y devuelve un bool , ¿cómo sabría el compilador que realmente quería un Func<string, bool> lugar de mi tipo de delegado? No puede inferir el tipo de delegado.


Eric Lippert tiene un viejo post respecto donde dice

Y, de hecho, la especificación C # 2.0 lo llama. Las expresiones de grupo de métodos y las expresiones de métodos anónimos son expresiones sin tipo en C # 2.0 y las expresiones lambda se unen en C # 3.0. Por lo tanto, es ilegal que aparezcan "desnudos" en el lado derecho de una declaración implícita.


Los siguientes puntos son de MSDN con respecto a las variables locales implícitamente tipadas:

  1. var solo se puede usar cuando una variable local se declara e inicializa en la misma declaración; la variable no se puede inicializar a nulo, a un grupo de métodos o a una función anónima.
  2. La palabra clave var ordena al compilador que infiera el tipo de la variable a partir de la expresión del lado derecho de la declaración de inicialización.
  3. Es importante entender que la palabra clave var no significa "variante" y no indica que la variable esté escrita a máquina de forma imprecisa o vinculada de forma tardía. Simplemente significa que el compilador determina y asigna el tipo más apropiado.

Referencia de MSDN: variables locales implícitamente tipadas

Teniendo en cuenta lo siguiente con respecto a los métodos anónimos:

  1. Los métodos anónimos le permiten omitir la lista de parámetros.

Referencia de MSDN: Métodos anónimos

Sospecho que dado que el método anónimo puede tener diferentes firmas de métodos, el compilador no puede inferir correctamente cuál sería el tipo más apropiado para asignar.


Otros ya han señalado que hay infinitos posibles tipos de delegados que podría haber querido decir; ¿Qué tiene de especial Func que merezca ser el predeterminado en lugar de Predicate o Action o cualquier otra posibilidad? Y, para lambdas, ¿por qué es obvio que la intención es elegir el formulario de delegado, en lugar de la forma de árbol de expresión?

Pero podríamos decir que Func es especial, y que el tipo inferido de un método lambda o anónimo es Func de algo. Todavía tendríamos todo tipo de problemas. ¿Qué tipos te gustaría inferir para los siguientes casos?

var x1 = (ref int y)=>123;

No hay ningún tipo de Func<T> que tome una referencia.

var x2 = y=>123;

No conocemos el tipo del parámetro formal, aunque conocemos el retorno. (¿O es que nosotros? ¿Es el retorno int? Long? Short? Byte?)

var x3 = (int y)=>null;

No conocemos el tipo de devolución, pero no puede ser nulo. El tipo de devolución podría ser cualquier tipo de referencia o cualquier tipo de valor que admite valores.

var x4 = (int y)=>{ throw new Exception(); }

De nuevo, no conocemos el tipo de devolución, y esta vez puede ser nulo.

var x5 = (int y)=> q += y;

¿Se pretende que sea una declaración de devolución de void lambda o algo que devuelva el valor asignado a q? Ambos son legales; ¿Qué deberíamos elegir?

Ahora, puede decir, bueno, simplemente no admite ninguna de esas características. Solo soporte casos "normales" donde los tipos se pueden resolver. Eso no ayuda. ¿Cómo eso me hace la vida más fácil? Si la función funciona a veces y falla a veces, aún tengo que escribir el código para detectar todas esas situaciones de falla y dar un mensaje de error significativo para cada una. Todavía tenemos que especificar todo ese comportamiento, documentarlo, escribir pruebas para él, etc. Esta es una característica muy costosa que le ahorra al usuario una media docena de pulsaciones de teclas. Tenemos mejores formas de agregar valor al lenguaje que pasar mucho tiempo escribiendo casos de prueba para una función que no funciona la mitad del tiempo y que no proporciona casi ningún beneficio en los casos en que funciona.

La situación en la que es realmente útil es:

var xAnon = (int y)=>new { Y = y };

porque no hay un tipo "hablable" para esa cosa. Pero tenemos este problema todo el tiempo, y solo usamos inferencia de tipo de método para deducir el tipo:

Func<A, R> WorkItOut<A, R>(Func<A, R> f) { return f; } ... var xAnon = WorkItOut((int y)=>new { Y = y });

y ahora la inferencia del tipo de método resuelve qué es el tipo de func.


Solo Eric Lippert lo sabe con certeza, pero creo que es porque la firma del tipo de delegado no determina de forma exclusiva el tipo.

Considera tu ejemplo:

var comparer = delegate(string value) { return value != "0"; };

Aquí hay dos posibles inferencias para lo que debería ser la var :

Predicate<string> comparer = delegate(string value) { return value != "0"; }; // okay Func<string, bool> comparer = delegate(string value) { return value != "0"; }; // also okay

¿Cuál debería inferir el compilador? No hay una buena razón para elegir uno u otro. Y aunque un Predicate<T> es funcionalmente equivalente a un Func<T, bool> , todavía son tipos diferentes en el nivel del sistema de tipo .NET. Por lo tanto, el compilador no puede resolver inequívocamente el tipo de delegado, y debe fallar la inferencia de tipo.