Новый ответ в свете ответа Ганса
Благодаря ответу Ханса мы видим, что реализация несколько сложнее, чем мы думали. И компилятор, и среда CLR очень стараются создать впечатление, что тип массива реализует, IList<T>
но дисперсия массива делает это сложнее. В отличие от ответа Ханса, типы массивов (одномерные, в любом случае с нулевым отсчетом) реализуют общие коллекции напрямую, потому что тип какого-либо конкретного массива не является System.Array
- это просто базовый тип массива. Если вы спросите тип массива, какие интерфейсы он поддерживает, он включает в себя общие типы:
foreach (var type in typeof(int[]).GetInterfaces())
{
Console.WriteLine(type);
}
Вывод:
System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
System.Collections.Generic.IList`1[System.Int32]
System.Collections.Generic.ICollection`1[System.Int32]
System.Collections.Generic.IEnumerable`1[System.Int32]
Для одномерных массивов, начинающихся с нуля, с точки зрения языка , массив действительно также реализуется IList<T>
. Об этом говорится в разделе 12.1.2 спецификации C #. Таким образом, что бы ни делала базовая реализация, язык должен вести себя так, как если бы этот тип T[]
реализован, IList<T>
как и любой другой интерфейс. С этой точки зрения, интерфейс будет реализован с некоторыми из членов которые явно реализованы (например Count
). Это лучшее объяснение происходящего на языковом уровне.
Обратите внимание, что это справедливо только для одномерных массивов (и массивов с нулевым отсчетом, а не то, что C # как язык говорит что-либо о ненулевых массивах). T[,]
не реализует IList<T>
.
С точки зрения CLR происходит нечто более забавное. Вы не можете получить отображение интерфейса для универсальных типов интерфейса. Например:
typeof(int[]).GetInterfaceMap(typeof(ICollection<int>))
Дает исключение:
Unhandled Exception: System.ArgumentException: Interface maps for generic
interfaces on arrays cannot be retrived.
Так к чему странность? Ну, я считаю, что это действительно связано с ковариацией массивов, которая является бородавкой в системе типов, ИМО. Несмотря на то, что ковариантность неIList<T>
является ковариантной (и не может быть безопасной), ковариация массива позволяет этому работать:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
... что делает его похожим на typeof(string[])
орудие труда IList<object>
, хотя на самом деле это не так.
Спецификация CLI (ECMA-335), раздел 1, раздел 8.7.1, имеет следующее:
Тип подписи T совместим с типом подписи U тогда и только тогда, когда выполняется хотя бы одно из следующего:
...
T - это массив с рангом 1, отсчитываемый от нуля V[]
, и U
равен IList<W>
, а V является совместимым по элементам массива с W.
(На самом деле здесь не упоминается ICollection<W>
или, как IEnumerable<W>
я считаю, ошибка в спецификации.)
В случае отсутствия вариативности спецификация CLI идет вместе со спецификацией языка. Из раздела 8.9.1 раздела 1:
Кроме того, созданный вектор с типом элемента T реализует интерфейс System.Collections.Generic.IList<U>
, где U: = T. (§8.7)
( Вектор - это одномерный массив с нулевым основанием.)
Теперь, что касается деталей реализации , ясно, что CLR выполняет некое фанковое отображение, чтобы сохранить здесь совместимость присваивания: когда a string[]
запрашивается реализация ICollection<object>.Count
, он не может справиться с этим вполне обычным способом. Считается ли это явной реализацией интерфейса? Я думаю, что разумно относиться к этому так, поскольку, если вы не запрашиваете отображение интерфейса напрямую, он всегда ведет себя таким образом с точки зрения языка.
О чем ICollection.Count
?
До сих пор я говорил об общих интерфейсах, но есть еще неуниверсальный ICollection
со своим Count
свойством. На этот раз мы можем получить отображение интерфейса, и на самом деле интерфейс реализуется напрямую с помощью System.Array
. В документации для ICollection.Count
реализации свойства Array
указано, что оно реализовано с явной реализацией интерфейса.
Если кто-нибудь может придумать, чем такая явная реализация интерфейса отличается от «нормальной» явной реализации интерфейса, я был бы счастлив изучить ее подробнее.
Старый ответ о явной реализации интерфейса
Несмотря на вышесказанное, что является более сложным из-за знания массивов, вы все равно можете делать что-то с теми же видимыми эффектами посредством явной реализации интерфейса .
Вот простой автономный пример:
public interface IFoo
{
void M1();
void M2();
}
public class Foo : IFoo
{
// Explicit interface implementation
void IFoo.M1() {}
// Implicit interface implementation
public void M2() {}
}
class Test
{
static void Main()
{
Foo foo = new Foo();
foo.M1(); // Compile-time failure
foo.M2(); // Fine
IFoo ifoo = foo;
ifoo.M1(); // Fine
ifoo.M2(); // Fine
}
}
Array
класс должен быть написан на C #!