Нова відповідь у світлі відповіді Ганса
Завдяки відповіді Ганса ми можемо бачити, що реалізація є дещо складнішою, ніж ми можемо подумати. І компілятор, і 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.
То чому дивацтво? Ну, я вважаю, що це дійсно пов'язано з коваріацією масиву, яка є бородавкою в системі типів, IMO. Незважаючи на те, що IList<T>
він не є коваріантним (і не може бути безпечним), коваріація масиву дозволяє це працювати:
string[] strings = { "a", "b", "c" };
IList<object> objects = strings;
... що робить його виглядати як typeof(string[])
знаряддя IList<object>
, коли це не на самому ділі.
Розділ 8.7.1 специфікації CLI (ECMA-335), розділ 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 робить певне відображення, щоб зберегти сумісність призначення тут: коли 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 #!