Как массивы в C# частично реализуют IList?
так как вы можете знать, массивы в C# реализовать IList<T> среди прочих интерфейсов. Так или иначе, они делают это без публичной реализации свойства Count of IList<T>! Массивы имеют только свойство длины.
это вопиющий пример того, как C#/.NET нарушает свои собственные правила о реализации интерфейса или я что-то упускаю?
6 ответов:
новый ответ в свете ответа
благодаря ответу, данному Гансом, мы можем видеть, что реализация несколько сложнее, чем мы могли бы подумать. И компилятор, и среда CLR пытаются очень тяжелый чтобы создать впечатление, что тип массива реализует
IList<T>- но дисперсия массива делает это хитрее. Вопреки ответу от Hans, типы массивов (одномерные, основанные на нуле в любом случае) реализуют общие коллекции напрямую, потому что тип любого конкретного массива не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>is не ковариантный (и не может быть безопасным), ковариация массива позволяет это работать:string[] strings = { "a", "b", "c" }; IList<object> objects = strings;... что делает его посмотреть как
typeof(string[])осуществляетIList<object>, когда это на самом деле не так.раздел 1 спецификации CLI (ECMA-335), раздел 8.7.1, имеет следующее:
тип подписи T совместим-с типом подписи U тогда и только тогда, когда хотя бы одно из следующих значений
...
T-это нулевой массив ранга-1
V[]иUиIList<W>, и V массив-элемент-совместимый-с W.(на самом деле он не упоминает
ICollection<W>илиIEnumerable<W>что я считаю ошибкой в спецификации.)для non-отклонения, спецификация CLI идет вместе с спецификацией языка сразу. Из раздела 8.9.1 раздела 1:
кроме того, созданный вектор с типом элемента T реализует интерфейс
System.Collections.Generic.IList<U>, где U : = T. (§8.7)(A вектор представляет собой одномерный массив с нулевым основанием.)
теперь с точки зрения детали реализации, очевидно, 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 } }
так как вы можете знать, массивы в C# реализовать
IList<T>, среди прочих интерфейсовНу да, ЭМ нет, не совсем. Это объявление для класса Array в .NET 4 framework:
[Serializable, ComVisible(true)] public abstract class Array : ICloneable, IList, ICollection, IEnumerable, IStructuralComparable, IStructuralEquatable { // etc.. }он реализует систему.Коллекции.Илист,не
IList<T>.Countосуществляется явно:int[] intArray = new int[10]; IList<int> intArrayAsList = (IList<int>)intArray; Debug.Assert(intArrayAsList.Count == 10);это делается так, что когда у вас есть простая переменная массива, у вас нет обоих
CountиLengthнепосредственно.в общем случае явная реализация интерфейса используется, когда вы хотите гарантировать, что тип может использоваться определенным образом, не заставляя всех потребителей типа думать об этом таким образом.
Edit: Упс, плохо помню там.
ICollection.Countявно реализован. ОбщийIList<T>обрабатывается как Ганс описывает ниже.
явная реализация интерфейса. Короче говоря, вы объявляете это как
void IControl.Paint() { }илиint IList<T>.Count { get { return 0; } }.
Это ничем не отличается от явной реализации интерфейса IList. Просто потому, что вы реализуете интерфейс, не означает, что его члены должны отображаться как члены класса. Это тут реализовать свойство Count, он просто не предоставляет его на X[].
при наличии справочных источников:
//---------------------------------------------------------------------------------------- // ! READ THIS BEFORE YOU WORK ON THIS CLASS. // // The methods on this class must be written VERY carefully to avoid introducing security holes. // That's because they are invoked with special "this"! The "this" object // for all of these methods are not SZArrayHelper objects. Rather, they are of type U[] // where U[] is castable to T[]. No actual SZArrayHelper object is ever instantiated. Thus, you will // see a lot of expressions that cast "this" "T[]". // // This class is needed to allow an SZ array of type T[] to expose IList<T>, // IList<T.BaseType>, etc., etc. all the way up to IList<Object>. When the following call is // made: // // ((IList<T>) (new U[n])).SomeIListMethod() // // the interface stub dispatcher treats this as a special case, loads up SZArrayHelper, // finds the corresponding generic method (matched simply by method name), instantiates // it for type <T> and executes it. // // The "T" will reflect the interface used to invoke the method. The actual runtime "this" will be // array that is castable to "T[]" (i.e. for primitivs and valuetypes, it will be exactly // "T[]" - for orefs, it may be a "U[]" where U derives from T.) //---------------------------------------------------------------------------------------- sealed class SZArrayHelper { // It is never legal to instantiate this class. private SZArrayHelper() { Contract.Assert(false, "Hey! How'd I get here?"); } /* ... snip ... */ }конкретно эта часть:
интерфейс заглушки диспетчера воспринимает это как особый случай, загружается Саррайхельпер, находит соответствующий общий метод (соответствует просто на имя метода), создает его для типа и выполняет его.
(выделено мной)
источник (свиток вверх.)
Comments