Метод расширения и динамический объект

96

Я собираюсь резюмировать свою проблему в следующем фрагменте кода.

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

Выше код работает нормально.

Теперь я попробовал следующее

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

но я получаю RuntimeBinderException. Почему это так?

Сантош Сингх
источник
Это похоже на дубликат этого вопроса,
заданного
@jbtule Разница в том, что thisздесь динамический, но если вы приземлитесь здесь, вам, вероятно, стоит посмотреть и на этот вопрос
nik.shornikov

Ответы:

131

Чтобы расширить ответ Stecya ... методы расширения не поддерживаются динамической типизацией в форме методов расширения , т.е. вызываются так, как если бы они были методами экземпляра. Однако это будет работать:

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

Конечно, это может быть полезно, а может и нет. Если бы вы могли предоставить дополнительную информацию о том, почему и как вы пытаетесь использовать динамическую типизацию, возможно, мы сможем вам больше помочь.

Джон Скит
источник
Я играл с динамическим объектом и получил это исключение.
Писали
19
@geek: Лично мое эмпирическое правило - использовать только dynamicтам, где вам действительно нужно ... в основном, если вы иначе обращались бы к членам с помощью отражения, это большой знак. С другой стороны, я упорный статичный типаж - другие могут предложить менее пессимистичную политику :)
Джон Скит
2
Может быть, было бы удобнее привести приведение к известному типу, это работает: Console.WriteLine (((List <int>) dList) .First ()); Или Console.WriteLine ((dList as List <int>) .First ()) ;.
AVee
139

Чтобы расширить ответ Джона, причина, по которой это не работает, заключается в том, что в обычных, нединамических методах расширения кода работают, выполняя полный поиск всех классов, известных компилятору, для статического класса, у которого есть соответствующий метод расширения. Поиск идет по порядку в зависимости от вложенности пространств имен и доступных usingдиректив в каждом пространстве имен.

Это означает, что для правильного разрешения вызова метода динамического расширения DLR каким-то образом должен знать во время выполнения, какие вложения и usingдирективы пространств имен были в вашем исходном коде . У нас нет удобного механизма для кодирования всей этой информации на сайте вызова. Мы думали об изобретении такого механизма, но решили, что это слишком дорого и сопряжено со слишком большим риском для графика, чтобы того стоить.

Эрик Липперт
источник
Большое спасибо за объяснение.
santosh Singh
3
В ближайшее время такая фича? Это определенно будет переломным моментом; вызовы, которые в настоящее время выбрасывают RunTimeBinderExceptions, внезапно начнут работать после перекомпиляции исходного кода. Кроме того, будут ли какие-либо риски безопасности, связанные с реализацией такой функции?
Ани
5
@ani: Планируем ли мы реализовать эту функцию? Нет. Есть ли риски для безопасности? Я ничего не знаю; какую угрозу безопасности вы имели в виду? Начните с того, что скажите, кто злоумышленник и какую угрозу он представляет для пользователя.
Эрик Липперт
@EricLippert, я понял, что все dynamicобъекты равны C # : DynamicObject, поэтому нет возможности их различать, и это одна из причин, по которой невозможно добавить методы расширения dynamic, верно?
Tom Sarduy
@EricLippert рассмотрит вопрос о том, чтобы расширить этот ответ еще немного и добавить предложение в строке «Когда любой из параметров является динамическим, все разрешения откладываются до времени выполнения». Хотя для вас очевидно, что этот важный бит трудно найти где-либо еще на SO (см., Например, stackoverflow.com/questions/48324768 )
Алексей Левенков
18

Потому что First()это не метод List. Он определен в расширении Linq дляIEnumerable<>

Стеця
источник