Зачем использовать явную реализацию интерфейса для вызова защищенного метода?

При просмотре исходного кода ASP.NET MVC в codeplex Я обнаружил, что обычно класс явно реализует интерфейс. Явно реализованный метод/свойство затем вызывает другой «защищенный виртуальный» метод/свойство с тем же именем.

Например,

public class MvcHandler : IHttpHandler, IRequiresSessionState 
{
    protected virtual bool IsReusable 
    {
        get 
        {
           return false;
        }
    }

    bool IHttpHandler.IsReusable 
    {
        get 
        {
           return IsReusable;
        }
    }
}

Теперь я уверен, в чем преимущество такого программирования. Лично я предпочитаю просто неявно реализовать интерфейс IHttpHandler.

Я предполагаю, что автор просто не хочет, чтобы MvcHandler имел общедоступное свойство IsResuable. Свойство IsReusable можно использовать, только если экземпляр MvcHandler обрабатывается как IHttpHandler. Тем не менее, я не уверен, почему автор что это путь.

Кто-нибудь знает больше преимуществ этого стиля реализации интерфейса?


person Morgan Cheng    schedule 11.11.2008    source источник
comment
Забавно, читая источник MVC ровно через 3 года после вашего первого поста, у меня возник точно такой же вопрос.   -  person ScottS    schedule 13.11.2011


Ответы (3)


Ну, не только для MVC, но этот подход позволяет вам поддерживать чистоту основного общедоступного API. Это также полезно, если когда-либо существует риск того, что разные интерфейсы / и т. Д. Имеют одно и то же имя и подпись, но разное значение. На самом деле это редкость.

Это также позволяет вам предоставить реализацию, в которой вы хотите, чтобы тип возвращаемого значения изменялся в подклассах:

(ICloneable выбран для простоты - не зацикливайтесь на том факте, что это плохо определенный интерфейс... лучшим примером были бы такие вещи, как DbCommand и т. д., которые делают это - но это сложнее показать на коротком примере )

class Foo : ICloneable
{
    public Foo Clone() { return CloneCore(); }
    object ICloneable.Clone() { return CloneCore(); }
    protected virtual Foo CloneCore() { ... }
}

class Bar : Foo
{
    protected override Foo CloneCore() { ... }
    public new Bar Clone() { return (Bar)CloneCore(); }
}

Если бы мы использовали общедоступный виртуальный метод, мы не смогли бы override его и использовать new в базовом классе, поскольку вам не разрешено делать и то, и другое:

class A
{
    public virtual A SomeMethod() { ... }
}
class B : A
{
    public override A SomeMethod() { ... }
    //Error 1   Type 'B' already defines a member called 'SomeMethod' with the same parameter types
    public new B SomeMethod() { ... }
}

Используя защищенный виртуальный подход, любое использование:

  • Фу.Клон()
  • Бар.Клон()
  • ICloneable.Clone()

все используют правильную реализацию CloneCore() для конкретного типа.

person Marc Gravell    schedule 11.11.2008

Если класс реализует IFoo.Bar явно, а производному классу требуется IFoo.Bar, чтобы сделать что-то другое, производный класс не сможет вызвать реализацию этого метода в базовом классе. Производный класс, который не повторно реализует IFoo.Bar, может вызывать реализацию базового класса через ((IFoo)this).Bar(), но если производный класс повторно реализует IFoo.Bar (что необходимо для изменения его поведения), вышеупомянутый вызов будет передан производному классу. -реализация класса, а не реализация базового класса. Даже ((IFoo)(BaseType)this).bar не поможет, так как приведение ссылки к типу интерфейса отбрасывает любую информацию о типе ссылки (в отличие от типа экземпляра экземпляра), который был приведен.

Если явная реализация интерфейса не делает ничего, кроме вызова защищенного метода, это позволяет избежать этой проблемы, поскольку производный класс может изменить поведение метода интерфейса, переопределив виртуальный метод, сохраняя при этом возможность вызывать базовую реализацию по своему усмотрению. ИМХО, С# должна была иметь явную реализацию интерфейса, создающую виртуальный метод с именем, совместимым с CLS, поэтому кто-то, пишущий на С# производную от класса, который явно реализовал IFoo.Bar, мог сказать override void IFoo.Bar, а кто-то, пишущий на каком-то другом языке, мог сказать, например, Overrides Sub Explicit_IFoo_Bar(); поскольку любой производный класс может повторно реализовать IFoo.Bar, и поскольку любой производный класс, который не повторно реализует IFoo.Bar, может вызывать его сам по себе, я не вижу какой-либо полезной цели в закрытии явной реализации.

Между прочим, в vb.net обычным шаблоном будет просто Protected Overridable Sub IFoo_Bar() Implements IFoo.Bar, без необходимости в отдельном виртуальном методе.

person supercat    schedule 06.01.2013

  1. Когда член реализован явно, к нему нельзя получить доступ через экземпляр класса, а только через экземпляр интерфейса. ссылка: Руководство по явной реализации интерфейса
  2. По моему опыту, если разработчик интерфейса явно реализует интерфейс, он получит ошибку компилятора после того, как вы удалите метод из интерфейса, в то время как он не уведомит, если он / она неявно реализует его, и метод останется в коде.

образец для причины 1:

public interface IFoo
{
    void method1();
    void method2();
}

public class Foo : IFoo
{
    // you can't declare explicit implemented method as public
    void IFoo.method1() 
    {
    }

    public void method2()
    {
    }

    private void test()
    {
        var foo = new Foo();
        foo.method1(); //ERROR: not accessible because foo is object instance
        method1(); //ERROR: not accessible because foo is object instance
        foo.method2(); //OK
        method2(); //OK

        IFoo ifoo = new Foo();
        ifoo.method1(); //OK, because ifoo declared as interface
        ifoo.method2(); //OK
    }
}
person Mohammad Nikravan    schedule 23.08.2012
comment
Ваша причина № 2 на самом деле очень хорошая, и ее часто упускают из виду. Если пользователи используют неявный интерфейс в вашем классе, и вы измените интерфейс, пользователи все равно будут вызывать старый метод. С явным интерфейсом этого не может произойти. - person Abel; 20.05.2014