W C # zacząłem widzieć wyskakujące wszystkie magiczne metody, bez tworzenia kopii zapasowej przez interfejs. Dlaczego został wybrany?
Pozwól mi wyjaśnić.
Poprzednio w języku C #, jeśli obiekt implementował IEnumerable
interfejs, byłby automatycznie iterowalny przez foreach
pętlę. Ma to dla mnie sens, ponieważ jest to wspierane przez interfejs, a jeśli miałbym mieć własną Iterator
funkcję wewnątrz iteracji klasy, mógłbym to zrobić bez obawy, że to magicznie oznaczałoby coś innego.
Teraz najwyraźniej (nie wiadomo kiedy) te interfejsy nie są już wymagane. Po prostu musi mieć odpowiednie konwersje nazw.
Innym przykładem jest spowodowanie, że każdy obiekt będzie oczekiwany dzięki metodzie o nazwie dokładnie, GetAwaiter
która ma kilka specyficznych właściwości.
Dlaczego nie stworzyć interfejsu takiego jak oni IEnumerable
lub INotifyPropertyChanged
statycznie poprzeć tę „magię”?
Więcej szczegółów na temat tego, co mam na myśli tutaj:
http://blog.nem.ec/2014/01/01/magic-methods-c-sharp/
Jakie są zalety i wady magicznych metod i czy jest gdziekolwiek w Internecie, gdzie mogę znaleźć informacje na temat przyczyn podjęcia tych decyzji?
async
/await
, to będzie on działał tylko z kodem, który został napisany po .NET 4.5 stał się wystarczająco rozpowszechniony, aby być realnym celem… co jest w zasadzie teraz. Ale czysto syntaktyczne tłumaczenie na wywołania metod pozwala mi dodaćawait
funkcjonalność do istniejących typów po fakcie.foreach
pętli na początku. Tam nigdy nie było wymagane dla obiektu w celu wdrożeniaIEnumerable
doforeach
do pracy. To tylko konwencja.Odpowiedzi:
Ogólnie „magiczne metody” stosuje się, gdy nie jest możliwe stworzenie interfejsu, który działałby w ten sam sposób.
Kiedy
foreach
po raz pierwszy został wprowadzony w C # 1.0 (takie zachowanie z pewnością nie jest niczym nowym), musiał używać magicznych metod, ponieważ nie było żadnych leków generycznych. Dostępne opcje to:Użyj nietypowego
IEnumerable
iIEnumerator
działającego zobject
s, co oznacza typy wartości boksu. Ponieważ iterowanie czegoś w rodzaju listyint
s powinno być bardzo szybkie i na pewno nie powinno tworzyć wielu wartości w skrzyniach na śmieci, nie jest to dobry wybór.Poczekaj na leki generyczne. Oznaczałoby to prawdopodobnie opóźnienie .Net 1.0 (lub przynajmniej
foreach
) o więcej niż 3 lata.Użyj magicznych metod.
Wybrali więc opcję nr 3 i została z nami ze względu na kompatybilność wsteczną, chociaż od .Net 2.0, wymaganie też
IEnumerable<T>
by działało.Inicjatory kolekcji mogą wyglądać inaczej dla każdego typu kolekcji. Porównaj
List<T>
:i
Dictionary<TKey, TValue>
:Nie możesz mieć jednego interfejsu, który obsługiwałby tylko pierwszy formularz
List<T>
i tylko drugi dlaDictionary<TKey, TValue>
.Metody LINQ są zwykle implementowane jako metody rozszerzenia (tak, że może istnieć tylko jedna implementacja np. LINQ do Object dla wszystkich typów, które implementują
IEnumerable<T>
), co oznacza, że nie można użyć interfejsu.Dla
await
TheGetResult()
sposób może powrócić albovoid
lub pewnego rodzajuT
. Ponownie, nie możesz mieć jednego interfejsu, który mógłby obsłużyć oba. Chociażawait
jest częściowo oparty na interfejsie: oczekujący musi zaimplementowaćINotifyCompletion
i może również zaimplementowaćICriticalNotifyCompletion
.źródło
foreach
C # 1.2 (nic nie ma w MSDN dla C # 1.0) mówi, że wyrażenie wforeach
„Ewaluuje do typu, który implementujeIEnumerable
lub typu, który deklarujeGetEnumerator
metodę”. I nie jestem pewien, co masz na myśli przez to, rozpakowywanie w C # jest zawsze jednoznaczne.foreach(T x in sequence)
stosuje jawne rzutowaniaT
na elementy sekwencji. Więc jeśli sekwencja jest zwykłaIEnumerable
iT
jest typem wartości, rozpakuje się bez zapisania żadnego jawnego rzutowania w twoim kodzie. Jedna z bardziej brzydkich części C #.foreach
.)