2010-01-02 18 views
16

Varias preguntas de C# en StackOverflow preguntan cómo hacer delegados/lambdas anónimos con los parámetros out o ref. Véase, por ejemplo:¿Por qué los delegados/lambdas anónimos no infieren los tipos en los parámetros out/ref?

Para ello, sólo tiene que especificar el tipo del parámetro, como en:

public void delegate D(out T p); 
// ... 
D a = (out T t) => { ... };  // Lambda syntax. 
D b = delegate(out T t) { ... }; // Anonymous delegate syntax. 

Lo Tengo curiosidad acerca de por qué el tipo se requiere explícitamente. ¿Hay alguna razón en particular para que este sea el caso? Es decir, desde el punto de vista del compilador/idioma, ¿por qué no se permite lo siguiente?

D a = (out t) => { ... };  // Lambda syntax -- implicit typing. 
D b = delegate(out t) { ... }; // Anonymous delegate syntax -- implicit typing. 

o incluso mejor, simplemente:

D a = (t) => { ... };  // Lambda syntax -- implicit typing and ref|out-ness. 
D b = delegate(t) { ... }; // Anonymous delegate syntax -- implicit typing and ref|out-ness. 
+3

Tengo curiosidad sobre esto yo mismo. Esperemos que Eric Lippert se dé cuenta de esta publicación ... es más probable que pueda dar una respuesta significativa a esto. – LBushkin

+2

LBushkin, si quiere llamar mi atención sobre algo, siempre puede enviarlo a través del enlace de contacto en mi blog. –

+0

Para los métodos anónimos con la palabra clave 'delegate', la regla es que o bien no especifica nada (omita incluso el paréntesis'() ') o especifica la firma completa incluyendo tipos de parámetros y modificadores como' ref'/'out'. Entonces 'ref' /' out' no es una excepción. Para lambdas, tu idea tiene sentido, pero no estoy seguro si creo que es buena. La sintaxis '(out t) => {...}' hace que parezca que "out" es el tipo del parámetro. Entonces 't => {...}' es mejor, pero podría ser "peligroso" si alguien olvida el significado de 'D' y edita el cuerpo' {...} 'de la lambda sin darse cuenta de' out' naturaleza de 't'. –

Respuesta

17

pregunta interesante.

En primer lugar, tenga en cuenta la diferencia entre los métodos anónimos y lambdas. Desde la perspectiva del escritor del compilador, la diferencia más importante es que lambdas puede requerir que el compilador infiera el tipo de parámetros del objetivo al que se le está asignando el lambda; Los métodos anónimos de C# 2 no tienen esta característica. Esta característica parece una pequeña diferencia, pero de hecho tiene importantes ramificaciones en la implementación del compilador. Ver mi serie del blog sobre este tema para algunos pensamientos acerca de por qué esto es:

http://blogs.msdn.com/ericlippert/archive/2007/01/10/lambda-expressions-vs-anonymous-methods-part-one.aspx

Así que ahora vamos a llegar a su pregunta real: ¿por qué no podemos inferir outness/refness del tipo de destino en los parámetros de la lambda. Es decir, si tenemos delegate void D (out int x) entonces seguramente D d = x => {x = 10; } podría inferir que x es "out int".

No hay ninguna razón técnica que sepa por qué no pudimos hacer eso. Internamente en el compilador, los tipos de salida/ref se representan como tipos como cualquier otro.

Sin embargo, las funciones no se realizan solo porque se pueden hacer; terminan porque hay una razón de peso para hacerlo.Para lambdas, la razón de peso para hacer inferencia de tipo en primer lugar es LINQ; queremos poder hacer una transformación sintáctica simple en una comprensión de consulta en una llamada de método con lambdas, y dejar que el motor de inferencia de tipo de método resuelva los tipos de todos los parámetros lambda. Ninguno de los métodos LINQ generados tiene delegados con parámetros out o ref.

Por lo tanto, no tenemos un motivo convincente para realizar la función. Los delegados que tienen parámetros out/ref son relativamente raros. Y la asignación de lambdas a esos delegados es aún más rara. Entonces esta es una característica que no necesitamos y que beneficia a casi nadie.

C# 3 era el "polo largo" en el calendario de Visual Studio; tuvimos la mayor cantidad de días de trabajo programados para cualquier equipo que envíe un componente en VS. Eso significaba que todos los días nos salteamos el cronograma, se deslizó toda la división . Eso fue un poderoso desincentivo para pasar el tiempo en funciones innecesarias que no beneficiaban a nadie. Entonces el trabajo nunca fue hecho.

Estoy de acuerdo en que sería bueno ser más consecuente aquí, pero es poco probable que suceda. Tenemos muchas prioridades más altas.

+1

Creo que todos son muy conscientes de lo ocupados que están ustedes, pero definitivamente es bueno saber que la puerta no está cerrada por una razón técnica, y que teóricamente es posible hacerlo algún día. Gracias por la entrada, Eric! –

2

De Eric Lippert's comment sobre por qué declaración y asignación de una variable var no se pueden dividir:

Estoy de acuerdo que, en principio, esto podría hacerse , pero en la práctica es bastante más complicado de lo que indicaría el boceto rápido. var no solo requiere que haya un inicializador, también requiere que el inicializador no se refiera a la variable. Si tiene int M (out int), puede decir "int x = M (out x);" pero no se puede decir "var x = M (out x);" porque para hacer una resolución de sobrecarga en M, necesitamos saber el tipo de x, que es lo que estamos tratando de resolver. ¿Sería legal decir "var s; if (b) M (out s); else s = 0;" ?

supongo que la respuesta a su pregunta es similar, teniendo en cuenta, por ejemplo,

D a = (out var x) => x = M(out x); 
+2

No estoy tan seguro. ¿Cómo explicas el hecho de que si eliminas la palabra clave 'out' el compilador puede hacer la inferencia? El tipo de delegado 'D' debe determinar completamente el tipo de' x'. – LBushkin

+1

No estoy convencido de que este sea el caso. Aquí, sabemos que estamos tratando de terminar con algo que parece una 'D'. Si el resultado de la derecha no coincide, ¿no será obvio? Con el ejemplo 'var', podría ser cualquier cosa, por lo que el compilador no tiene ninguna pista sobre cómo debería ser la" respuesta correcta ". [¡Vaya! ¡LBushkin me ganó al golpe!] –

Cuestiones relacionadas