Perché sono stati implementati metodi magici in C #?


16

In C #, ho iniziato a vedere spuntare tutti questi metodi magici, senza essere supportato da un'interfaccia. Perché è stato scelto?

Lasciatemi spiegare.

Precedentemente in C #, se un oggetto implementava l' IEnumerableinterfaccia, sarebbe automaticamente iterabile da un foreachciclo. Questo ha senso per me, dato che è supportato da un'interfaccia e se dovessi avere la mia Iteratorfunzione all'interno della classe in fase di iterazione, potrei farlo senza preoccuparmi che significherebbe magicamente qualcos'altro.

Ora, a quanto pare, (non sono sicuro di quando), queste interfacce non sono più necessarie. Deve solo avere le giuste conversioni di denominazione.

Un altro esempio è rendere qualsiasi oggetto attendibile avendo un metodo chiamato esattamente GetAwaiter che ha alcune proprietà specifiche.

Perché non creare un'interfaccia come hanno fatto con IEnumerableo INotifyPropertyChangedper sostenere questa "magia" staticamente?

Maggiori dettagli su cosa intendo qui:

http://blog.nem.ec/2014/01/01/magic-methods-c-sharp/

Quali sono i pro e i contro dei metodi magici, e c'è un posto online dove posso trovare qualcosa sul perché queste decisioni sono state prese?


4
Dovresti modificare la tua opinione personale sul perché "la magia è cattiva" se non vuoi essere chiuso.
DougM,

2
Se vuoi sapere perché Anders Hejlsberg lo ha fatto, dovrai chiederglielo. Posso solo dirti perché lo avrei fatto, ed è per compatibilità con il futuro. I metodi di estensione consentono di "falsare" l'aggiunta di metodi ai tipi esistenti, ma non esistono interfacce di estensione. Se hai bisogno di un'interfaccia per, diciamo, async/ await, funzionerà solo con il codice che è stato scritto dopo che .NET 4.5 è diventato abbastanza diffuso da essere un obiettivo praticabile ... che è praticamente adesso. Ma una traduzione puramente sintattica in chiamate di metodo mi consente di aggiungere awaitfunzionalità ai tipi esistenti dopo il fatto.
Jörg W Mittag,

2
Si noti che questo è fondamentalmente applicato all'orientamento agli oggetti: fintanto che un oggetto risponde ai messaggi appropriati, è considerato del tipo corretto.
Jörg W Mittag,

7
Il tuo "precedentemente" e "ora" sono all'indietro - il metodo magico è stato implementato come funzionalità di base per un foreachloop all'inizio. Non è mai stato richiesto che l'oggetto implementasse IEnumerableper foreachfunzionare. È stato solo un convegno farlo.
Jesse C. Slicer,

2
@gbulmer è qui che ricordo di aver avuto l'idea da: blogs.msdn.com/b/ericlippert/archive/2011/06/30/… (e in misura minore ericlippert.com/2013/07/22/… )
Jesse C. Slicer,

Risposte:


16

In generale, vengono utilizzati "metodi magici" quando non è possibile creare un'interfaccia che funzioni allo stesso modo.

Quando è foreachstato introdotto per la prima volta in C # 1.0 (quel comportamento non è certo nulla di recente), ha dovuto usare metodi magici, perché non c'erano generici. Le opzioni erano sostanzialmente:

  1. Usa il non generico IEnumerablee IEnumeratorfunziona con objects, il che significa tipi di valore di inscatolamento. Dal momento che l'iterazione di qualcosa come un elenco di ints dovrebbe essere molto veloce e certamente non dovrebbe creare molti valori "garbage box", questa non è una buona scelta.

  2. Aspetta i generici. Ciò significherebbe probabilmente ritardare .Net 1.0 (o almeno foreach) di oltre 3 anni.

  3. Usa metodi magici.

Quindi, hanno scelto l'opzione n. 3 ed è rimasta con noi per motivi di compatibilità con le versioni precedenti, anche se dal momento che .Net 2.0, IEnumerable<T>avrebbe richiesto che avrebbe funzionato anche.


Gli inizializzatori di raccolta possono avere un aspetto diverso su ciascun tipo di raccolta. Confronta List<T>:

public void Add(T item)

e Dictionary<TKey, TValue>:

public void Add(TKey key, TValue value)

Non è possibile avere un'unica interfaccia che supporti solo il primo modulo List<T>e solo il secondo per Dictionary<TKey, TValue>.


I metodi LINQ sono di solito implementati come metodi di estensione (in modo che ci possa essere una sola implementazione di ad esempio LINQ to Object per tutti i tipi che implementano IEnumerable<T>), il che significa che non è possibile utilizzare un'interfaccia.


Per await, Il GetResult()metodo può restituire uno voido alcuni tipi T. Ancora una volta, non è possibile avere un'unica interfaccia in grado di gestire entrambi. Sebbene awaitsia parzialmente basato sull'interfaccia: il cameriere deve implementare INotifyCompletione può anche implementare ICriticalNotifyCompletion.


1
Sei sicuro "hanno scelto l'opzione # 3" per C # 1.0? Il mio ricordo è che era l'opzione 1. La mia memoria è che C # rivendicava una superiorità significativa su Java perché il compilatore C # faceva "auto-boxing" e "auto-unboxing". È stato affermato, C # ha reso il codice come quello foreach funziona molto meglio di Java in parte a causa di ciò.
Bagliore

1
La documentazione per foreachin C # 1.2 (non c'è nulla su MSDN per C # 1.0) dice che l'espressione in foreach"Valuta un tipo che implementa IEnumerableo un tipo che dichiara un GetEnumeratormetodo". E non sono sicuro di cosa tu voglia dire con questo, unboxing in C # è sempre esplicito.
svick

1
@gbulmer E la specifica ECMA per C # del dicembre 2001 (che deve significare che è per C # 1.0) dice anche che (§ 15.8.4): “Si dice che un tipo C sia un tipo di raccolta se implementa l'interfaccia System.IEnumerable o implementa il modello di raccolta soddisfacendo tutti i seguenti criteri […] ”.
svick

1
@svick foreach(T x in sequence)applica cast espliciti agli Telementi della sequenza. Quindi, se la sequenza è semplice IEnumerableed Tè un tipo di valore, verrà decompressa senza che il cast esplicito venga scritto nel codice. Una delle parti più brutte di C #.
CodesInCos

@CodesInChaos "Auto-unboxing" suona piuttosto generale, non mi rendevo conto che faceva riferimento a questa specifica funzionalità. (Immagino che avrei dovuto, visto che stavamo parlando foreach.)
svick
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.