Monday, December 03, 2012

Немного об интерфейсах в .Net (по мотивам одного интервью)

В прошедний понедельник мне посчастливилось попасть на собеседование на 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: Inline image 1

Posted via email from Jasper-Net