Почему нижеследующий код работает?
int[] ixs = new int[10];
int cnt = ((IReadOnlyCollection
Как это вообще компилируется? Ведь у массива нет свойства Count (у него есть Length и LongLength), соответственно - он не должен приводиться к IReadOnlyCollection
Ответ
Свойства Count у массива и правда нету. Но для того, чтобы имплементировать интерфейс, оно и не нужно.
Есть такая штука в .NET — explicit interface implementation. Это когда класс имплементирует интерфейс, но не заводит метод с нужным именем. (Она и применяется в случае массива.)
Пример. Пусть у нас есть такой интерфейс:
interface I { void Run(); }
Тогда мы можем сделать так:
class C : I
{
public void Run() { ... }
}
А можем и так:
class C : I
{
void I.Run() { ... }
}
При этом интерфейс имплементирован в обоих случаях, но в первом случае у нас есть публичный метод Run, а во втором нету, и этот метод можно вызвать только через каст к нужному интерфейсу.
Для чего такая фича языка (explicit interface implementation) реально нужна? А вот для чего. Представьте себе, что вы хотите реализовать в классе два интерфейса, которые конфликтуют между собой, определяют функцию или свойство с одинаковым именем, которые нужно для разных интерфейсов реализовывать по-разному. Тогда вы можете написать две разные имплементации:
class C : IExecutioner, IAsyncModelObject
{
Task IExecutioner.Execute() { /* казним приговорённого */ }
Task IAsyncModelObject.Execute() { /* начинаем работу в фоне */ }
}
Любой массив в .NET произведён от System.Array. Самой имплементации конкретных массивов в исходниках фреймворка нету, она генерируется автоматически. Документация говорит:
Starting with the .NET Framework 2.0, the Array class implements the System.Collections.Generic.IList
В реальности, начиная с .NET 4.5, добавлены тем же механизмом ещё два интерфейса: System.Collections.Generic.IReadOnlyList
Посмотрим интерфейсы, реализуемые типом System.Array и конкретным типом массива int[]. Такой код:
foreach (var i in typeof(System.Array).GetInterfaces())
Console.WriteLine(i);
выдаёт
System.ICloneable
System.Collections.IList
System.Collections.ICollection
System.Collections.IEnumerable
System.Collections.IStructuralComparable
System.Collections.IStructuralEquatable
В то же время
foreach (var i in typeof(int[]).GetInterfaces())
Console.WriteLine(i);
выдаёт
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]
System.Collections.Generic.IReadOnlyList`1[System.Int32]
System.Collections.Generic.IReadOnlyCollection`1[System.Int32]
Итак, лишь конкретные массивы имплементируют IReadOnlyCollection
Окей, но откуда же берётся сам код свойства? Заглянем в исходники .NET (Array -- базовый класс всех массивов) видим:
public abstract class Array : ICloneable, IList, IStructuralComparable, IStructuralEquatable
{
// ...
int ICollection.Count
{ get { return Length; } }
Таким образом, свойства Count действительно нет, есть лишь свойство ICollection.Count, которое доступно только через каст к интерфейсу. Это показывает, откуда взялась реализация ICollection, но что же насчёт IReadOnlyCollection
Из исходников
// This class is needed to allow an SZ array of type T[] to expose IList
Комментариев нет:
Отправить комментарий