В прошедний понедельник мне посчастливилось попасть на собеседование на Senior .Net Developer в одну международную компанию. Во время собеседования мне предложили пройти тест, где ряд вопросов был связан с .Net. В частности в одном из вопросов надо было дать оценку (истина/ложь) ряду утверждений, среди которых было и такое:
В .Net любой массив элементов, например int[], по умолчанию реализует IList, что позволяет использовать его в качестве коллекции в операторе foreach.
Быстро ответив на этот вопрос отрицательно и отдельно дописав на полях. что для foreach необходима реализация не IList, а IEnumerable, я перешел к следующему вопросу. Однако по дороге домой меня мучал вопрос: реализует ли массив все-таки этот интерфейс или нет?
Про IList я смутно помнил, что этот интерфейс дает мне IEnumerable, индексатор и свойство Count, содержащее число элементов коллекции, а также еще пару редко используемых свойств, типа IsFixedCollection(). Массив имеет свойство Length для своего размера, а Count в IEnumerable является методом расширения от LINQ, что было бы невозможно, если бы этот метод был реализован в классе. Таким образом, получалось, что массив не мог реализовывать интерфейс IList, однако какое-то смутное чувство не давало мне покоя. Поэтому вечером после интервью я решил провести небольшое исследование.
Класс System.Array
Поскольку Reflector.Net у меня не был установлен, я просто написал короткую программку на С# чтобы узнать, что за интерфейсы реализуются целочисленным массивом.
var v = new int[] { 1, 2, 3 };
var t = v.GetType();
var i = t.GetInterfaces();
foreach(var tp in i)
Console.WriteLine(tp.Name);
Вот полный список полученных интерфейсов из окна консоли:
ICloneable
IList
ICollection
IEnumerable
IStructuralComparable
IStructuralEquatable
IList`1
ICollection`1
IEnumerable`1
IReadOnlyList`1
IReadOnlyCollection`1
...
...
Всё сразу встает на свои места, когда вспоминаешь о том, что в С# можно реализовывать интерфейсы не только
неявно (implicit), но и явно (explicit). Я знал об этой возможности из учебников, где приводился пример такой имплементации в случае. когда базовый класс уже содержит метод с тем же именем, что и метод интерфейса. Я также видел такую возможность в ReSharper. Однако до настоящего времени мне напрямую не приходилось сталкиваться с необходимостью явной реализации интерфейсов в моих собственных проектах.
Сравнение явной и неявной реализации интерфейсов
Давайте сравним эти два вида реализации интерфейсов:.
Критерии | Неявная (implicit) реализация | Явная (explicit) реализация |
---|---|---|
Базовый синтаксис | interface ITest { void DoTest(); } public class ImplicitTest : ITest { public void DoTest() { } } | interface ITest { void DoTest(); } public class ExplicitTest : ITest { void ITest.DoTest() { } } |
Видимость | Неявная имплементация всегда являелся открытой (public), поэтому к методам и свойствам можно обращаться напрямую.var imp = new ImplicitTest(); imp.DoTest(); | Явная имплементация всегда закрыта (private). Чтобы получить доступ к имплементации необходимо кастовать инстанцию класса к интерфейсу (upcast to interface). var exp = new ExplicitTest(); ((ITest)exp).DoTest(); |
Полиморфия | Неявная имплементация интерфейса может быть виртуальной (virtual), что позволяет переписывать эту имплементацию в классах-потомках. | Явная имплементация всегда статична. Она не может быть переписана (override) или перекрыта (new) в классах-потомках. Прим. 1 |
Абстрактный класс и реализация | Неявная реализация может быть абстрактной и реализовываться только в классе-потомке. | Явная реализация не может быть абстрактной, но сам класс может иметь другие абстрактные методы и сам быть абстрактным.Прим. 2 |
Read more: Habrahabr.ru
QR: