En C #, j'ai commencé à voir toutes ces méthodes magiques surgir, sans être sauvegardées par une interface. Pourquoi a-t-il été choisi?
Laissez-moi expliquer.
Auparavant en C #, si un objet implémentait l' IEnumerable
interface, il serait automatiquement itérable par une foreach
boucle. Cela a du sens pour moi, car il est soutenu par une interface, et si je devais avoir ma propre Iterator
fonction dans la classe en cours d'itération, je pourrais le faire sans craindre que cela signifierait par magie autre chose.
Maintenant, apparemment, (je ne sais pas quand), ces interfaces ne sont plus nécessaires. Il a juste besoin d'avoir les bonnes conversions de noms.
Un autre exemple est de rendre n'importe quel objet attendable en ayant une méthode nommée exactement GetAwaiter
qui a quelques propriétés spécifiques.
Pourquoi ne pas créer une interface comme ils l'ont fait avec IEnumerable
ou INotifyPropertyChanged
sauvegarder cette "magie" statiquement?
Plus de détails sur ce que je veux dire ici:
http://blog.nem.ec/2014/01/01/magic-methods-c-sharp/
Quels sont les avantages et les inconvénients des méthodes magiques, et y a-t-il un endroit en ligne où je peux trouver des informations sur les raisons pour lesquelles ces décisions ont été prises?
async
/await
, cela ne fonctionnera qu'avec le code qui a été écrit après que .NET 4.5 est devenu suffisamment répandu pour être une cible viable ... ce qui est fondamentalement maintenant. Mais une traduction purement syntaxique en appels de méthode me permet d'ajouter desawait
fonctionnalités aux types existants après coup.foreach
boucle au début. Il n'a jamais été nécessaire que l'objet à implémenterIEnumerable
pourforeach
fonctionner. C'est juste une convention de le faire.Réponses:
En général, des «méthodes magiques» sont utilisées lorsqu'il n'est pas possible de créer une interface qui fonctionnerait de la même manière.
Quand il a
foreach
été introduit pour la première fois en C # 1.0 (ce comportement n'est certainement pas récent), il a dû utiliser des méthodes magiques, car il n'y avait pas de génériques. Les options étaient essentiellement:Utilisez le non générique
IEnumerable
etIEnumerator
cela fonctionne avecobject
s, ce qui signifie des types de valeur de boxe. Étant donné que l'itération de quelque chose comme une liste deint
s devrait être très rapide et ne devrait certainement pas créer beaucoup de valeurs de boîte à ordures, ce n'est pas un bon choix.Attendez les génériques. Cela signifierait probablement retarder .Net 1.0 (ou au moins
foreach
) de plus de 3 ans.Utilisez des méthodes magiques.
Donc, ils ont choisi l'option # 3 et elle est restée avec nous pour des raisons de compatibilité descendante, même si depuis .Net 2.0, l'exigence
IEnumerable<T>
aurait également fonctionné.Les initialiseurs de collection peuvent avoir un aspect différent sur chaque type de collection. Comparez
List<T>
:et
Dictionary<TKey, TValue>
:Vous ne pouvez pas avoir une seule interface qui prendrait en charge uniquement le premier formulaire pour
List<T>
et uniquement le second pourDictionary<TKey, TValue>
.Les méthodes LINQ sont généralement implémentées en tant que méthodes d'extension (de sorte qu'il ne peut y avoir qu'une seule implémentation de LINQ to Object pour tous les types qui l'implémentent
IEnumerable<T>
), ce qui signifie qu'il n'est pas possible d'utiliser une interface.Pour
await
, laGetResult()
méthode peut renvoyervoid
un type ou un typeT
. Encore une fois, vous ne pouvez pas avoir une seule interface capable de gérer les deux. Bien qu'ilawait
soit partiellement basé sur l'interface: l'attente doit implémenterINotifyCompletion
et peut également implémenterICriticalNotifyCompletion
.la source
foreach
C # 1.2 (il n'y a rien sur MSDN pour C # 1.0) dit que l'expression dansforeach
"Evalue à un type qui implémenteIEnumerable
ou à un type qui déclare uneGetEnumerator
méthode". Et je ne suis pas sûr de ce que vous entendez par là, déballer en C # est toujours explicite.foreach(T x in sequence)
applique des transtypages explicites auxT
éléments de la séquence. Par conséquent, si la séquence est un simpleIEnumerable
etT
est un type de valeur, elle sera décompressée sans qu'une conversion explicite ne soit écrite dans votre code. L'une des parties les plus laides de C #.foreach
.)