En C #, comencé a ver aparecer todos estos métodos mágicos, sin una copia de seguridad de una interfaz. ¿Por qué fue elegido esto?
Dejame explicar.
Anteriormente en C #, si un objeto implementaba la IEnumerable
interfaz, sería iterable automáticamente por un foreach
bucle. Eso tiene sentido para mí, ya que está respaldado por una interfaz, y si tuviera que repetir mi propia Iterator
función dentro de la clase, podría hacerlo sin preocuparme de que mágicamente significara otra cosa.
Ahora, aparentemente, (no estoy seguro de cuándo), estas interfaces ya no son necesarias. Solo necesita tener las conversiones de nombres correctas.
Otro ejemplo es hacer que cualquier objeto sea aceptable al tener un método llamado exactamente GetAwaiter
que tiene algunas propiedades específicas.
¿Por qué no hacer una interfaz como lo hicieron con ellos IEnumerable
o INotifyPropertyChanged
para respaldar esta "magia" estáticamente?
Más detalles sobre lo que quiero decir aquí:
http://blog.nem.ec/2014/01/01/magic-methods-c-sharp/
¿Cuáles son los pros y los contras de los métodos mágicos? ¿Existe algún sitio en línea donde pueda encontrar algo sobre por qué se tomaron estas decisiones?
async
/await
, eso solo funcionará con el código que se escribió después de que .NET 4.5 se extendió lo suficiente como para ser un objetivo viable ... que es básicamente ahora. Pero una traducción puramente sintáctica en llamadas a métodos me permite agregarawait
funcionalidad a los tipos existentes después del hecho.foreach
bucle al principio. Hay que nunca ha sido un requisito para el objeto de implementarIEnumerable
paraforeach
el trabajo. Ha sido una convención hacerlo.Respuestas:
En general, los "métodos mágicos" se utilizan cuando no es posible crear una interfaz que funcione de la misma manera.
Cuando
foreach
se introdujo por primera vez en C # 1.0 (ese comportamiento ciertamente no es nada reciente), tuvo que usar métodos mágicos, porque no había genéricos. Las opciones básicamente fueron:Utilice el no genérico
IEnumerable
yIEnumerator
que funcione conobject
s, lo que significa tipos de valores de boxeo. Dado que iterar algo como una lista deint
s debería ser muy rápido y ciertamente no debería crear muchos valores en caja de basura, esta no es una buena opción.Espera genéricos. Esto probablemente significaría retrasar .Net 1.0 (o al menos
foreach
) por más de 3 años.Usa métodos mágicos.
Por lo tanto, eligieron la opción n. ° 3 y se quedó con nosotros por razones de compatibilidad con versiones anteriores, aunque desde .Net 2.0, requerirlo
IEnumerable<T>
también habría funcionado.Los inicializadores de colección pueden verse diferentes en cada tipo de colección. Compara
List<T>
:y
Dictionary<TKey, TValue>
:No puede tener una única interfaz que admita solo el primer formulario
List<T>
y solo el segundoDictionary<TKey, TValue>
.Los métodos LINQ generalmente se implementan como métodos de extensión (de modo que puede haber una sola implementación de, por ejemplo, LINQ to Object para todos los tipos que implementan
IEnumerable<T>
), lo que significa que no es posible usar una interfaz.Para
await
, ElGetResult()
método puede devolver unovoid
o algún tipoT
. Nuevamente, no puede tener una sola interfaz que pueda manejar ambas. Aunqueawait
está parcialmente basado en la interfaz: el camarero tiene que implementarINotifyCompletion
y también puede implementarICriticalNotifyCompletion
.fuente
foreach
en C # 1.2 (no hay nada en MSDN para C # 1.0) dice que la expresión enforeach
"Evalúa a un tipo que implementaIEnumerable
o un tipo que declara unGetEnumerator
método". Y no estoy seguro de qué quiere decir con eso, unboxing en C # siempre es explícito.foreach(T x in sequence)
aplica conversiones explícitas aT
los elementos de la secuencia. Entonces, si la secuencia es simpleIEnumerable
yT
es un tipo de valor, se desempaquetará sin que se escriba ningún molde explícito en su código. Una de las partes más feas de C #.foreach
).