Может ли дочерний класс реализовать тот же интерфейс, что и его родитель?
Я никогда не сталкивался с этой проблемой до сегодняшнего дня и задавался вопросом, какой конвенцией/лучшей практикой для достижения такого поведения было бы.
Основная настройка:
public interface IDispatch {
void Dispatch();
}
public class Foo : IDispatch {
void IDispatch.Dispatch() {
DoSomething();
}
}
public class Bar : Foo {
...
}
Бар нуждается в подклассе Foo, потому что он обладает всеми теми же свойствами, что и Bar plus, представляет два новых, с которыми мне нужно столкнуться. Проблема в том, что Foo также нуждается в немного другой реализации Dispatch(). Обычно это было бы переопределено, но это не относится к методу интерфейса, так что это прекрасно, просто чтобы Bar реализовать IDispatch, так что мое определение класса выглядит следующим образом:
public class Bar : Foo, IDispatch { .... }
а затем просто выполните явную реализацию этого интерфейса в Bar? Мой компилятор, похоже, не жалуется, когда я пытаюсь сделать это таким образом, но я не был уверен, вызовет ли он какие-либо проблемы во время выполнения, какие реализации использовать в будущем, или если есть лучший способ добиться чего-то подобного.
Также стоит упомянуть, что на моем рабочем месте мы используем генерацию кода из UML-моделей, которая предусматривает, что дизайн всех классов должен выполняться сначала из модели. Инструмент генерации кода - это то, что заставляет методы интерфейса быть реализованы явно (не хочу обсуждать плюсы и минусы этого именно того, к чему я вынужден заниматься прямо сейчас, поэтому наличие неявной реализации не является вариантом)
Ответы
Ответ 1
Вы также можете сделать это одним из двух способов:
Во-первых, не реализуйте интерфейс явно:
public class Foo : IDispatch {
public virtual void Dispatch() {
whatever();
}
}
public class Bar : Foo {
public override void Dispatch() {
whateverElse();
}
}
Во-вторых, реализуйте его явно, но добавьте функцию, которую может переопределить дочерний класс:
public class Foo : IDispatch {
void IDispatch.Dispatch() {
this.Dispatch();
}
protected virtual void Dispatch() {
whatever();
}
}
public class Bar : Foo {
protected override void Dispatch() {
whateverElse();
}
}
Ответ 2
Да, вы можете явно переопределить, что хотите реализовать IDispatch
, и реализовать его явно в Bar
.
Однако, вы не сможете вызвать исходную реализацию в Foo
. Если вам нужно это сделать, вам нужно будет изменить Foo
либо на использование реализации неявного интерфейса с помощью виртуального метода (который можно переопределить, а затем вызвать с помощью base.Dispatch()
в Bar
)) или выполнить реализацию Foo
вызовите защищенный виртуальный метод, который вы снова переопределите в Bar
.
Ответ 3
Бар уже реализует IDispatch, если он является подклассом Foo, не нужно явно указывать это. Если вы хотите реализовать только один метод интерфейса по-другому, выполните следующие действия:
IDispatch { void Method(); }
Foo : IDispatch { public virtual void Method() { implementation1 } }
Bar : Foo { public override void Method() { implementation2 } }
Ответ 4
Вам не нужно делать IDispatch.Dispatch
- если в вашем классе используется метод Dispatch
, вы должны реализовать интерфейс.
Вы можете сделать это, он строит для меня:
public class Foo : IDispatch
{
public virtual void Dispatch()
{
}
}
public class Bar : Foo
{
public override void Dispatch()
{
base.Dispatch();
}
}
Ответ 5
Я предпочитаю явно реализовывать интерфейсы. Людям, не знакомым с вашей кодовой базой, легче понять, что такое интерфейс по сравнению с логикой, специфичной для класса.
Вы все еще можете выполнить наследование классов при реализации интерфейсов. Вам просто нужно, чтобы базовый класс реализовывал интерфейс, и чтобы этот вызов реализации превращался в виртуальную функцию, которая может быть расширена. Вот пример:
interface Inter
{
void Call();
}
class A : Inter
{
void Inter.Call()
{
this.Call();
}
public virtual void Call() { Console.WriteLine("Base call in A"); }
}
class B : A
{
public override void Call()
{
Console.WriteLine( "Called B" );
}
}
class Program
{
static void Main( string[] args )
{
var a = new A(); //Base class
var aa = (Inter)a; //Interface only
a.Call();
aa.Call();
var b = new B(); //Child class
var bb = (Inter)b; //Interface only of Child class
b.Call();
bb.Call();
//See the output before the console program closes
Console.ReadLine();
}
}
Вывод программы:
Base call in A
Base call in A
Called B
Called B