Может ли дочерний класс реализовать тот же интерфейс, что и его родитель?

Я никогда не сталкивался с этой проблемой до сегодняшнего дня и задавался вопросом, какой конвенцией/лучшей практикой для достижения такого поведения было бы.

Основная настройка:

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