В С#, почему реализации интерфейса должны реализовать другую версию метода явно?

Возьмем этот пример:

public interface IFoo
{
    IFoo Bar();
}

public class Foo : IFoo
{
    public Foo Bar()
    {
        //...
    }

    IFoo IFoo.Bar() { return Bar(); } //Why is this necessary?
}

Почему подразумевается неявная реализация IFoo Bar(), даже если Foo преобразуется в IFoo без литья?

Ответ 1

Вы можете решить это так (немного уродливо, но заботится о сильном вводе):

public interface IFoo<T> where T : IFoo<T>
{
    T Bar();
}

public class Foo : IFoo<Foo>
{
    public Foo Bar()
    {
        //...
    }
}

Ответ 2

Microsoft имеет подробную запись по этому вопросу, но это сводится к реализации нескольких интерфейсов/классов, которые имеют в них один и тот же метод, Implicit больше не работает в этом контексте.

class Test 
{
    static void Main()
    {
        SampleClass sc = new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

        // The following lines all call the same method.
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interface IControl
{
    void Paint();
}
interface ISurface
{
    void Paint();
}
class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method.  
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

// Output: 
// Paint method in SampleClass 
// Paint method in SampleClass 
// Paint method in SampleClass

Если бы мы приняли явный подход, мы закончим с этим.

public class SampleClass : IControl, ISurface
{
    void IControl.Paint()
    {
        System.Console.WriteLine("IControl.Paint");
    }
    void ISurface.Paint()
    {
        System.Console.WriteLine("ISurface.Paint");
    }
}

Все это сводится к обеспечению уникальности, когда сталкиваются реализованные типы. В вашем примере Foo есть IFoo.

Ответ 3

В этом случае он нужен, потому что С# не поддерживает ковариацию возвращаемого типа для интерфейсов, поэтому ваша функция

public Foo Bar()
{
    //...
}

не удовлетворяет интерфейсу IFoo, поскольку тип возврата метода Bar отличается.

Поскольку вы также хотите реализовать интерфейс, ваш единственный выбор - сделать это явно, поскольку у вас уже есть метод Bar(), определенный в классе.

Ответ 4

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

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

Ответ 5

Я бы рекомендовал сохранить скрытую реализацию как защищенную, а не публичную.

public class Foo : IFoo
{
    **protected virtual** Foo Bar()
    {
        //...
    }

    IFoo IFoo.Bar() { return Bar(); } 
}

существует довольно обширный ответ для того, почему/когда использовать явную реализацию в этом потоке:

неявная vs явная реализация интерфейса

Хорошей причиной для использования явной реализации является то, что вы можете легко использовать инъекцию зависимостей, чтобы иметь более свободное соединение, когда вы используете свой класс Foo.