2013-06-24 2 views
24

Давайте предположим, что у меня есть абстрактный базовый класс что-то простое, какабстрактные базовые классы, которые реализуют интерфейс

abstract class Item : IDisplayable 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
     public abstract void Print(); 

    } 

и у меня есть класс, который наследуется от как

class Chair: Item 
{ 
    public int NumberOfLegs {get;set;} 

    public void Print() 
    { 
    Console.WriteLine("Here is a simple interface implementation"); 
    } 
} 

interface IDisplayable 
{ 
void Print(); 
} 

дочерний класс делает явно не говорят, что он также реализует интерфейс, и все же он будет делать это через простое наследование. Если мы явно добавим интерфейс в дочерние классы, программа будет работать одинаково (по крайней мере, насколько я могу судить по моим простым примерам). Явно ли реализация интерфейса была бы хорошей или плохой идеей, или это строго вопрос предпочтения?

+0

ли вы имеете в виду 'Председатель: Item'? – neontapir

+0

Что такое 'IDisplayable'? –

+0

@ DanielA.White просто с помощью метода Console.WriteLine – wootscootinboogie

ответ

43

Если мы явно добавим интерфейс к дочерним классам, программа будет работать одинаково (по крайней мере, насколько я могу судить в моих простых примерах).

Программа не обязательно будет работать одинаково; ваших примеров недостаточно, чтобы проиллюстрировать разницу.

Будет ли явная реализация интерфейса хорошей или плохой идеей, или это строго вопрос предпочтения?

Это плохая идея, если вы не намерены обеспечить семантику интерфейс повторной реализации.

Позвольте мне вкратце проиллюстрировать. Что делает эта программа?

using System; 
interface IFoo { void Bar(); void Baz(); } 
class Alpha : IFoo 
{ 
    void IFoo.Bar() 
    { 
     Console.WriteLine("Alpha.Bar"); 
    } 
    void IFoo.Baz() 
    { 
     Console.WriteLine("Alpha.Baz"); 
    } 
} 
class Bravo : Alpha 
{ 
    public void Baz() 
    { 
     Console.WriteLine("Bravo.Baz"); 
    } 
} 
class CharlieOne : Bravo 
{ 
    public void Bar() 
    { 
     Console.WriteLine("CharlieOne.Bar"); 
    } 
} 
class CharlieTwo : Bravo, IFoo 
{ 
    public void Bar() 
    { 
     Console.WriteLine("CharlieTwo.Bar"); 
    } 
} 
class Program 
{ 
    static void Main() 
    { 
     IFoo foo = new Alpha(); 
     foo.Bar(); 
     foo.Baz(); 
     foo = new Bravo(); 
     foo.Bar(); 
     foo.Baz(); 
     foo = new CharlieOne(); 
     foo.Bar(); 
     foo.Baz(); 
     foo = new CharlieTwo(); 
     foo.Bar(); 
     foo.Baz(); 
    } 
} 

Прежде чем читать дальше, серьезно: попытаться предсказать результат этой программы.

Теперь на самом деле запустите его. Получили ли вы результат, ожидаемый? Где ваша интуиция была неправильной?

Вы видите разницу между CharlieOne и CharlieTwo? Повторная реализация IFoo в CharlieTwo может привести к переходу интерфейса Bravo.Baz, хотя Bravo делает не re-implement IFoo!

А с другой стороны: если вы ожидали Bravo.Baz быть назначен слот интерфейса только потому, что он существует, то вы видите, как неудачу повторно реализовать интерфейс вызывает код некорректным. Для Bravo.Baz для замены Alpha.IFoo.Baz, Bravo необходимо повторно выполнить IFoo.

вынос здесь: , когда вы повторно реализовать интерфейс, все привязок интерфейса пересчитывается с нуля. Это может привести к семантическим изменениям в вашей программе, поэтому только повторно реализует интерфейс, если вы хотите сделать это.

Это также иллюстрирует еще одну форму отказа ломкости базового класса . Предположим, у Bravo нет метода Baz, когда вы пишете Charlie. Если вы пишете Charlie повторно реализовать IFoo то автор Bravo добавив Bazпосле - возможно, авторы Bravo находятся на другой команды в вашей компании - изменения привязок интерфейса внутри Charlie даже если это не то, что авторы Bravo.

Для получения дополнительной информации см моей статьи на эту тему:

http://blogs.msdn.com/b/ericlippert/archive/2011/12/08/so-many-interfaces-part-two.aspx

+1

Очень благодарен вам за то, что потратил время, чтобы заставить людей посторонних использовать технологии 1 – wootscootinboogie

+1

@wootscootinboogie: Добро пожаловать; это мое удовольствие. И я многому учусь на этом сайте о том, как люди используют продукт, и как они ошибаются. –

4

С Item унаследовано от IDisplayable, все, что происходит от Item, также должно быть выполнено IDisplayable. Таким образом, явное добавление IDisplayable к этим классам является избыточным.

EDIT: @EricLippert делает превосходную информацию о влиянии повторного внедрения интерфейсов. Я оставляю этот ответ, чтобы сохранить обсуждение в комментариях.

+0

Легко, я не искал ни одного потрясающего откровения, в основном просто условного и реального мира. – wootscootinboogie

+0

Это избыточно, но в некоторых случаях полезно сделать это, возможно, чтобы понять, что класс реализует? (Лично, я думаю, что это не * полезно ...) –

+2

С общим названием, как 'Item', я согласен с @MatthewWatson. Чтобы этого избежать, я бы рекомендовал использовать более конкретный термин из вашего домена, что означает, что элемент отображается. Даже «DisplayableItem» станет улучшением. – neontapir

0

Когда вы говорите «явно реализуя интерфейс», я предполагаю, что вы подразумеваете включение интерфейса в объявление класса. Существует другое конкретное значение для явного внедрения интерфейса. Говоря это, объявляя, что производный класс реализует интерфейс, реализуемый базовым классом, неверен, хотя компилятор разрешит его, , потому что производный класс не содержит реализации этого интерфейса. Рассмотрим, что произойдет, если базовый класс явно реализован интерфейс -

abstract class Item : IDisplayable 
{ 
    void IDisplayable.Print() { ... } 
} 

Теперь производный класс не имеет видимости в метод базового класса и не занимайтесь потребителей производного класса. Конечно, потребители должны потреблять интерфейс, а не конкретный производный класс, а затем иметь доступ к явному методу интерфейса. Но в рамках самого производного класса он не знает интерфейсов, реализуемых базовым классом. Только то, что некоторые члены являются абстрактными, защищенными или виртуальными.