Итак, как вы, возможно, знаете, массивы в C # реализуются IList<T>
среди других интерфейсов. Однако каким-то образом они делают это без публичной реализации свойства Count IList<T>
! У массивов есть только свойство Length.
Это вопиющий пример того, как C # /. NET нарушает собственные правила реализации интерфейса, или я чего-то упускаю?
Array
класс должен быть написан на C #!Array
это «волшебный» класс, который не может быть реализован на C # или другом языке, ориентированном на .net. Но эта особенность доступна в C #.Ответы:
Новый ответ в свете ответа Ганса
Благодаря ответу Ханса мы видим, что реализация несколько сложнее, чем мы думали. И компилятор, и среда CLR очень стараются создать впечатление, что тип массива реализует,
IList<T>
но дисперсия массива делает это сложнее. В отличие от ответа Ханса, типы массивов (одномерные, в любом случае с нулевым отсчетом) реализуют общие коллекции напрямую, потому что тип какого-либо конкретного массива не являетсяSystem.Array
- это просто базовый тип массива. Если вы спросите тип массива, какие интерфейсы он поддерживает, он включает в себя общие типы:Вывод:
Для одномерных массивов, начинающихся с нуля, с точки зрения языка , массив действительно также реализуется
IList<T>
. Об этом говорится в разделе 12.1.2 спецификации C #. Таким образом, что бы ни делала базовая реализация, язык должен вести себя так, как если бы этот типT[]
реализован,IList<T>
как и любой другой интерфейс. С этой точки зрения, интерфейс будет реализован с некоторыми из членов которые явно реализованы (напримерCount
). Это лучшее объяснение происходящего на языковом уровне.Обратите внимание, что это справедливо только для одномерных массивов (и массивов с нулевым отсчетом, а не то, что C # как язык говорит что-либо о ненулевых массивах).
T[,]
не реализуетIList<T>
.С точки зрения CLR происходит нечто более забавное. Вы не можете получить отображение интерфейса для универсальных типов интерфейса. Например:
Дает исключение:
Так к чему странность? Ну, я считаю, что это действительно связано с ковариацией массивов, которая является бородавкой в системе типов, ИМО. Несмотря на то, что ковариантность не
IList<T>
является ковариантной (и не может быть безопасной), ковариация массива позволяет этому работать:... что делает его похожим на
typeof(string[])
орудие трудаIList<object>
, хотя на самом деле это не так.Спецификация CLI (ECMA-335), раздел 1, раздел 8.7.1, имеет следующее:
...
(На самом деле здесь не упоминается
ICollection<W>
или, какIEnumerable<W>
я считаю, ошибка в спецификации.)В случае отсутствия вариативности спецификация CLI идет вместе со спецификацией языка. Из раздела 8.9.1 раздела 1:
( Вектор - это одномерный массив с нулевым основанием.)
Теперь, что касается деталей реализации , ясно, что CLR выполняет некое фанковое отображение, чтобы сохранить здесь совместимость присваивания: когда a
string[]
запрашивается реализацияICollection<object>.Count
, он не может справиться с этим вполне обычным способом. Считается ли это явной реализацией интерфейса? Я думаю, что разумно относиться к этому так, поскольку, если вы не запрашиваете отображение интерфейса напрямую, он всегда ведет себя таким образом с точки зрения языка.О чем
ICollection.Count
?До сих пор я говорил об общих интерфейсах, но есть еще неуниверсальный
ICollection
со своимCount
свойством. На этот раз мы можем получить отображение интерфейса, и на самом деле интерфейс реализуется напрямую с помощьюSystem.Array
. В документации дляICollection.Count
реализации свойстваArray
указано, что оно реализовано с явной реализацией интерфейса.Если кто-нибудь может придумать, чем такая явная реализация интерфейса отличается от «нормальной» явной реализации интерфейса, я был бы счастлив изучить ее подробнее.
Старый ответ о явной реализации интерфейса
Несмотря на вышесказанное, что является более сложным из-за знания массивов, вы все равно можете делать что-то с теми же видимыми эффектами посредством явной реализации интерфейса .
Вот простой автономный пример:
источник
Count
- это нормально, ноAdd
всегда будет выбрасывать, поскольку массивы имеют фиксированный размер.Ну да, эм нет, не совсем. Это объявление класса Array в платформе .NET 4:
Он реализует System.Collections.IList, а не System.Collections.Generic.IList <>. Не может, Array не является универсальным. То же самое касается универсальных интерфейсов IEnumerable <> и ICollection <>.
Но среда CLR создает конкретные типы массивов на лету, так что технически она может создать массив, реализующий эти интерфейсы. Однако это не так. Попробуйте, например, этот код:
Вызов GetInterfaceMap () не выполняется для конкретного типа массива с сообщением «Интерфейс не найден». Однако приведение к IEnumerable <> работает без проблем.
Это шарлатанский набор текста. Это тот же тип ввода, который создает иллюзию, что каждый тип значения является производным от ValueType, производного от Object. И компилятор, и среда CLR обладают специальными знаниями о типах массивов, как и о типах значений. Компилятор видит вашу попытку преобразования в IList <> и говорит: «Хорошо, я знаю, как это сделать!». И испускает инструкцию castclass IL. У CLR нет проблем с этим, она знает, как предоставить реализацию IList <>, которая работает с базовым объектом массива. Он имеет встроенные сведения о скрытом в противном случае классе System.SZArrayHelper, оболочке, которая фактически реализует эти интерфейсы.
Что явно не так, как утверждают все, свойство Count, о котором вы спрашивали, выглядит следующим образом:
Да, этот комментарий можно назвать "нарушением правил" :) В остальном он чертовски удобен. И очень хорошо скрытый, вы можете проверить это в SSCLI20, дистрибутиве с общим исходным кодом для CLR. Найдите «IList», чтобы увидеть, где происходит замена типа. Лучше всего увидеть его в действии - это метод clr / src / vm / array.cpp, GetActualImplementationForArrayGenericIListMethod ().
Такая замена в CLR довольно мягкая по сравнению с тем, что происходит в языковой проекции в CLR, которая позволяет писать управляемый код для WinRT (также известного как Metro). Здесь заменяется практически любой базовый тип .NET. IList <> отображается на IVector <>, например, полностью неуправляемый тип. Сам по себе подстановка, COM не поддерживает универсальные типы.
Что ж, это был взгляд на то, что происходит за занавеской. Это могут быть очень неудобные, странные и незнакомые моря с драконами, живущими в конце карты. Может быть очень полезно сделать Землю плоской и смоделировать другое изображение того, что на самом деле происходит в управляемом коде. Так удобно сопоставить его со всеми любимыми ответами. Что не так хорошо работает для типов значений (не изменяйте структуру!), Но это очень хорошо скрыто. Ошибка метода GetInterfaceMap () - единственная утечка в абстракции, о которой я могу думать.
источник
Array
класса, который не является типом массива. Это базовый тип массива. Одномерный массив в C # действительно реализуетIList<T>
. И неуниверсальный тип, безусловно, может реализовать общий интерфейс в любом случае ... который работает, потому что существует множество разных типов -typeof(int[])
! = Typeof (string []), so
typeof (int []) `реализуетIList<int>
иtypeof(string[])
реализуетIList<string>
.Array
(которое, как вы показываете, является абстрактным классом, поэтому не может быть фактическим типом объекта массива), так и вывод (который он не реализуетIList<T>
) неверны IMO. Способ , в котором реализуетсяIList<T>
необычно и интересно, я согласен - но это чисто реализация деталей. Утверждать, чтоT[]
это не реализованоIList<T>
, вводит в заблуждение ИМО. Это противоречит спецификации и всему наблюдаемому поведению.IList<T>
потомуArray
что не выполняет? Эта логика - большая часть того, с чем я не согласен. Помимо этого, я думаю, нам нужно согласовать определение того, что означает для типа реализация интерфейса: на мой взгляд, типы массивов отображают все наблюдаемые особенности типов, которые реализуютсяIList<T>
, кромеGetInterfaceMapping
. Опять же, то, как это достигается, для меня менее важно, точно так же, как я могу сказать, чтоSystem.String
это неизменяемо, даже если детали реализации отличаются.IList<T>
.IList<T>.Count
реализовано явно :Это сделано для того, чтобы при наличии простой переменной массива у вас не было сразу и того,
Count
и другогоLength
.В общем случае явная реализация интерфейса используется, когда вы хотите гарантировать, что тип можно использовать определенным образом, не заставляя всех потребителей этого типа думать об этом таким образом.
Изменить : Упс, там плохой отзыв.
ICollection.Count
реализовано явно. РодовымIList<T>
обрабатываются как Hans descibes ниже .источник
string
).ICollection
объявляетCount
, и было бы еще больше запутать, если бы тип со словом «коллекция» в нем не использовалCount
:). При принятии этих решений всегда есть компромиссы.IList<T>
, несмотря на то, что и язык, и спецификации CLI говорят об обратном. Осмелюсь сказать, что способ, которым реализация интерфейса работает под прикрытием, может быть запутанным, но так бывает во многих ситуациях. Вы бы также проголосовали против кого-то, кто сказал, чтоSystem.String
это неизменяемое, только потому, что внутренняя работа изменяема? Для всех практических целей - и , конечно же , насколько C # язык обеспокоен - это является явным осущ.Явная реализация интерфейса . Короче декларируете как
void IControl.Paint() { }
илиint IList<T>.Count { get { return 0; } }
.источник
Это ничем не отличается от явной реализации интерфейса IList. Тот факт, что вы реализуете интерфейс, не означает, что его члены должны отображаться как члены класса. Он действительно реализует свойство Count, но не предоставляет его в X [].
источник
При наличии справочников:
В частности, эта часть:
(Акцент мой)
Источник (прокрутите вверх).
источник