visual ventajas sirve sharp que programacion para lenguaje historia descargar caracteristicas antecedentes c# compiler-construction idisposable duck-typing

ventajas - Pato escribiendo en el compilador C#



ventajas de c# (3)

Nota: Esta no es una pregunta acerca de cómo implementar o emular la tipificación de pato en C # ...

Durante varios años tuve la impresión de que ciertas características del lenguaje C # eran depdendent en las estructuras de datos definidas en el idioma en sí (que siempre me pareció un extraño escenario de pollo y huevo). Por ejemplo, tenía la impresión de que el ciclo foreach solo estaba disponible para usar con los tipos que implementaban IEnumerable .

Desde entonces, he llegado a comprender que el compilador de C # usa el tipado de pato para determinar si un objeto se puede usar en un ciclo foreach, buscando un método GetEnumerator lugar de IEnumerable . Esto tiene mucho sentido ya que elimina el acertijo del pollo y el huevo.

Estoy un poco confundido sobre por qué no es así, no parece ser el caso con el using bloque y IDisposable . ¿Hay alguna razón en particular por la que el compilador no pueda usar la tipificación de pato y busque un método de Dispose ? ¿Cuál es el motivo de esta incoherencia?

Tal vez hay algo más pasando bajo el capó con IDisposable?

Discutir por qué alguna vez tendrías un objeto con un método Dispose que no implementó IDisposable está fuera del alcance de esta pregunta :)


No hay nada especial sobre IDisposable aquí, pero hay algo especial sobre los iteradores.

Antes de C # 2, usar este tipo de pato en foreach era lo único que se podía implementar con un iterador fuertemente tipado, y también era la única forma de iterar sobre tipos de valores sin boxeo. Sospecho que si C # y .NET tuvieran genéricos para empezar, foreach habría requerido IEnumerable<T> lugar, y no tenía el pato escribiendo.

Ahora el compilador utiliza este tipo de pato escribiendo en un par de otros lugares en los que puedo pensar:

  • Los inicializadores de colecciones buscan una sobrecarga de Add adecuada (así como el tipo que tiene que implementar IEnumerable , solo para mostrar que realmente es una colección de algún tipo); esto permite la adición flexible de elementos individuales, pares clave / valor, etc.
  • LINQ ( Select etc.): así es como LINQ logra su flexibilidad, permitiendo el mismo formato de expresión de consulta contra múltiples tipos, sin tener que cambiar IEnumerable<T> sí mismo
  • Las expresiones de espera de C # 5 requieren que GetAwaiter devuelva un tipo de awaiter que tiene IsCompleted / OnCompleted / GetResult

En ambos casos, esto hace que sea más fácil agregar la característica a los tipos e interfaces existentes, donde el concepto no existía anteriormente.

Dado que IDisposable ha estado en el marco desde la primera versión, no creo que haya ningún beneficio en pato al escribir la sentencia using . Sé que trataste explícitamente de descartar las razones para disponer de Dispose sin implementar IDisposable de la discusión, pero creo que es un punto crucial. Debe haber buenas razones para implementar una característica en el lenguaje, y yo diría que el tipado de pato es una característica que va más allá de respaldar una interfaz conocida. Si no hay un beneficio claro al hacerlo, no terminará en el idioma.


No hay pollo ni huevo: foreach podría depender de IEnumerable ya que IEnumerable no depende de foreach . La razón por la cual se permite foreach en las colecciones que no se implementan. IEnumerable es probablemente en gran parte histórico :

En C #, no es estrictamente necesario que una clase de colección herede de IEnumerable e IEnumerator para ser compatible con foreach; siempre que la clase tenga los elementos requeridos GetEnumerator, MoveNext, Reset y Current, funcionará con foreach. Omitir las interfaces tiene la ventaja de permitirle definir el tipo de retorno de la Corriente para que sea más específico que el objeto, proporcionando así seguridad de tipo.

Además, no todos los problemas de huevo y gallina son realmente problemas: por ejemplo, una función puede llamarse a sí misma (¡recursividad!) O un tipo de referencia puede contenerse (como una lista vinculada).

Entonces, cuando se using , ¿por qué iban a usar algo tan complicado de especificar como pato escribiendo cuando simplemente pueden decir: implementar IDisposable ? Fundamentalmente, al usar el tipado de pato, se está ejecutando un ciclo final alrededor del sistema de tipo, que solo es útil cuando el sistema de tipo es insuficiente (o poco práctico) para abordar un problema.


La pregunta que estás preguntando no es una situación de gallina y huevo. Es más como cómo se implementa el compilador de lenguaje. Como el compilador C # y VB.NET se implementan de forma diferente. Si escribe un código simple de hello world y lo compila con el compilador e inspecciona el código IL, será diferente. Volviendo a su pregunta, me gustaría explicar qué código de IL genera el compilador de C # para IEnumerable .

IEnumerator e = arr.GetEnumerator(); while(e.MoveNext()) { e.Currrent; }

Entonces, el compilador C # se ajusta para el caso de foreach .