Утка набрав в компиляторе C#
Примечание это не вопрос о том, как реализовать или эмулировать ввод утки в C#...
в течение нескольких лет у меня было впечатление, что некоторые функции языка C# были depdendent на структурах данных, определенных в самом языке (который всегда казался мне странным сценарием курицы и яйца). Например, у меня сложилось впечатление, что foreach цикл был доступен только для использования с типами, которые реализованы IEnumerable.
С тех пор я понял, что компилятор C# использует duck typing, чтобы определить, можно ли использовать объект в цикле foreach, ища GetEnumerator метод, а не IEnumerable. Это имеет большой смысл, поскольку он удаляет загадку курицы и яйца.
Я немного смущен, почему это не похоже на случай с using блок а IDisposable. Есть ли какая-то особая причина, по которой компилятор не может использовать duck typing & look for a Dispose метод? В чем причина такого несоответствия?
возможно, что-то еще происходит под капотом с IDisposable?
обсуждая, почему вы бы когда-нибудь наличие объекта с методом Dispose, который не реализовал IDisposable, выходит за рамки этого вопроса :)
3 ответов:
в этом нет ничего особенного
IDisposableздесь - но там и что-то особенное про итераторы.перед C# 2, используя этот тип утки на
foreachбыл только вы могли бы реализовать строго типизированный итератор, а также единственный способ итерации по типам значений без бокса. Я подозреваемый что если бы C# и .NET имели дженерики для начала,foreachбы требуютсяIEnumerable<T>вместо этого и не было утиной типизацией.теперь компилятор использует такой тип утки в нескольких других местах, которые я могу придумать:
- инициализаторы коллекции ищут подходящий
Addперегрузка (а также тип, который нужно реализоватьIEnumerable, просто чтобы показать, что это действительно какая-то коллекция); это позволяет гибко добавлять отдельные элементы, пары ключ/значение и т. д.- LINQ (
Selectetc) - это как LINQ достигает своей гибкости, позволяющ этим же формат выражения запроса для нескольких типов, без необходимости изменять- выражения ожидания C# 5 требуют
GetAwaiterчтобы вернуть тип ожидающего, который имеетIsCompleted/OnCompleted/GetResultв обоих случаях это упрощает добавление функции к существующим типам и интерфейсам, где концепция ранее не существовала.
учитывая, что
IDisposableв рамках с самой первой версии, я не думаю, что есть было бы какое-то преимущество в утке набравusingзаявление. Я знаю, что вы явно пытались дисконтировать причины наличияDisposeбез реализацииIDisposableиз обсуждения, но я думаю, что это решающий момент. Там должны быть веские причины для реализации функции в языке, и я бы сказал, что утка типизация является функцией выше и за пределами поддержки известного интерфейса. Если нет явной выгоды в этом, это не будет в конечном итоге на языке.
нет курицы и яйца:
foreachзависит отIEnumerableСIEnumerableне зависит отforeach. Причина foreach разрешена для коллекций, не реализующихIEnumerableнаверное во многом исторический:в C#, это не является строго необходимым для наследования класса коллекции IEnumerable и IEnumerator по порядку чтобы быть совместимым с foreach; как долго как класс имеет необходимые GetEnumerator, MoveNext, Reset и Текущие члены, он будет работать с инструкция foreach. Опуская интерфейсы имеет преимущество позволять вам к определите тип возврата тока в будьте более конкретны, чем объект, тем самым обеспечение безопасности типов.
кроме того, не все проблемы с курицей и яйцом на самом деле являются проблемами: например, функция может вызвать себя (рекурсия!) или ссылочный тип может содержать себя (например, связанный список).
когда
usingпришел вокруг почему они используют что-то как сложно указать, как утка набрав, когда они могут просто сказать: реализоватьIDisposable? В принципе, используя duck typing, вы выполняете конечный запуск вокруг системы типов, что полезно только тогда, когда система типов недостаточна (или непрактична) для решения проблемы.
вопрос, который вы задаете, не является ситуацией с курицей и яйцом. Его больше похоже на то, как реализован компилятор языка. Как C# и VB.NET компиляторы реализуются по-разному.Если вы напишете простой код hello world и скомпилируете его с помощью компилятора и проверите код IL, они будут разными. Возвращаясь к вашему вопросу, я хотел бы объяснить, какой код IL генерируется компилятором C# для
IEnumerable.IEnumerator e = arr.GetEnumerator(); while(e.MoveNext()) { e.Currrent; }таким образом, компилятор C# настраивается для этого случая из
foreach.
Comments