#c_sharp #net
Почему нижеследующий код работает? int[] ixs = new int[10]; int cnt = ((IReadOnlyCollection)ixs).Count; Как это вообще компилируется? Ведь у массива нет свойства Count (у него есть Length и LongLength), соответственно - он не должен приводиться к IReadOnlyCollection .
Ответы
Ответ 1
Свойства 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, System.Collections.Generic.ICollection , and System.Collections.Generic.IEnumerable generic interfaces. The implementations are provided to arrays at run time, and as a result, the generic interfaces do not appear in the declaration syntax for the Array class. В реальности, начиная с .NET 4.5, добавлены тем же механизмом ещё два интерфейса: System.Collections.Generic.IReadOnlyList and System.Collections.Generic.IReadOnlyCollection . Все эти интерфейсы реализует не класс Array, а производные от него классы, представляющие собой массивы конкретного типа. Посмотрим интерфейсы, реализуемые типом 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 , сам System.Array этого не делает. Окей, но откуда же берётся сам код свойства? Заглянем в исходники .NET (Array -- базовый класс всех массивов) видим: public abstract class Array : ICloneable, IList, IStructuralComparable, IStructuralEquatable { // ... int ICollection.Count { get { return Length; } } Таким образом, свойства Count действительно нет, есть лишь свойство ICollection.Count, которое доступно только через каст к интерфейсу. Это показывает, откуда взялась реализация ICollection, но что же насчёт IReadOnlyCollection ? Как правильно отмечает @andreycha в соседнем ответе, для реализации generic-интерфейсов используется немного компиляторной магии и класс SZArrayHelper. Из исходников: // This class is needed to allow an SZ array of type T[] to expose IList , // IList , etc., etc. all the way up to IList
Комментариев нет:
Отправить комментарий