Ответ 1
Если мы явно добавим интерфейс к дочерним классам, программа будет работать одинаково (по крайней мере, насколько я могу судить в моих простых примерах).
Программа не обязательно будет работать одинаково; ваших примеров недостаточно, чтобы проиллюстрировать разницу.
Явно ли реализация интерфейса была бы хорошей или плохой идеей или это было бы строго вопросом предпочтения?
Это плохая идея, если вы не намерены обеспечить семантику повторной реализации интерфейса.
Позвольте мне вкратце проиллюстрировать. Что делает эта программа?
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
не выполняет повторное выполнение 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