Metoda rozszerzenia i obiekt dynamiczny

96

Zamierzam podsumować mój problem w następującym fragmencie kodu.

List<int> list = new List<int>() { 5, 56, 2, 4, 63, 2 };
Console.WriteLine(list.First());

Powyższy kod działa poprawnie.

Teraz wypróbowałem następujące

dynamic dList = list;
 Console.WriteLine(dList.First());

ale otrzymuję wyjątek RuntimeBinderException.Dlaczego tak jest?

santosh singh
źródło
Wygląda na to, że jest to duplikat tego pytania
zadanego
@jbtule Różnica polega na tym, że thistutaj jest dynamiczny, ale jeśli tu wylądujesz, prawdopodobnie powinieneś również spojrzeć na to pytanie
nik.shornikov

Odpowiedzi:

131

Aby rozwinąć odpowiedź Stecyi ... metody rozszerzające nie są obsługiwane przez dynamiczne wpisywanie w postaci metod rozszerzających , tj. Wywoływane tak, jakby były metodami instancji. Jednak to zadziała:

dynamic dList = list;
Console.WriteLine(Enumerable.First(dList));

Oczywiście może to być przydatne lub nie. Jeśli możesz podać więcej informacji o tym, dlaczego i jak próbujesz używać dynamicznego pisania, być może będziemy mogli bardziej pomóc.

Jon Skeet
źródło
Bawiłem się
19
@geek: Osobiście moją praktyczną zasadą jest używanie tylko dynamictam, gdzie naprawdę potrzebujesz ... w zasadzie, jeśli w innym przypadku miałbyś mieć dostęp do członków z refleksją, to duży znak. Z drugiej strony jestem zagorzałym statecznikiem - inni mogą sugerować mniej pesymistyczną politykę :)
Jon Skeet
2
Bardziej czytelne może być rzutowanie z powrotem do znanego typu, działa to: Console.WriteLine (((List <int>) dList) .First ()); Lub Console.WriteLine ((dList as List <int>) .First ()) ;.
AVee
138

Aby rozwinąć odpowiedź Jona, powodem, dla którego to nie działa, jest to, że w zwykłych, niedynamicznych metodach rozszerzających kod działa, wykonując pełne wyszukiwanie wszystkich klas znanych kompilatorowi dla klasy statycznej, która ma pasującą metodę rozszerzającą. Wyszukiwanie przebiega w kolejności na podstawie zagnieżdżenia przestrzeni nazw i dostępnych usingdyrektyw w każdej przestrzeni nazw.

Oznacza to, że aby wywołanie metody rozszerzenia dynamicznego zostało poprawnie rozwiązane, DLR musi w jakiś sposób wiedzieć w czasie wykonywania, jakie zagnieżdżenia przestrzeni nazw i usingdyrektywy znajdują się w kodzie źródłowym . Nie mamy przydatnego mechanizmu do kodowania wszystkich tych informacji w witrynie wywołania. Zastanawialiśmy się nad wynalezieniem takiego mechanizmu, ale zdecydowaliśmy, że jest to zbyt kosztowne i powoduje zbyt duże ryzyko związane z harmonogramem, aby było tego warte.

Eric Lippert
źródło
Bardzo dziękuję za wyjaśnienie.
santosh singh
3
Czy taka funkcja jest w zasięgu ręki? Z pewnością byłaby to przełomowa zmiana; wywołania aktualnie generujące RunTimeBinderExceptions zaczęłyby nagle działać po ponownej kompilacji źródła. Czy byłoby też jakieś zagrożenie bezpieczeństwa związane z implementacją takiej funkcji?
Ani
5
@ani: Czy planujemy wdrożyć tę funkcję? Nie. Czy są jakieś zagrożenia bezpieczeństwa? Nie znam żadnego; jakie zagrożenie bezpieczeństwa miałeś na myśli? Zacznij od określenia, kto jest atakującym i jakie zagrożenie stanowi dla użytkownika.
Eric Lippert
@EricLippert, zrozumiałem, że wszystkie dynamicobiekty są równe C # :, DynamicObjectwięc nie ma sposobu na ich rozróżnienie i jest to jeden z powodów, dla których nie jest możliwe dodanie metod rozszerzających do dynamic, prawda?
Tom Sarduy
@EricLippert rozważ rozszerzenie tej odpowiedzi nieco bardziej i dodanie zdania w stylu „Gdy którykolwiek z parametrów jest dynamiczny, wszystkie rozdzielczości są odkładane do czasu wykonania”. Chociaż jest dla ciebie oczywiste, ten ważny fragment jest trudny do znalezienia nigdzie indziej w SO (patrz na przykład stackoverflow.com/questions/48324768 )
Alexei Levenkov
18

Ponieważ First()nie jest to metoda List. Jest zdefiniowany w Linq Extension toIEnumerable<>

Stecya
źródło