Зачем использовать явную реализацию интерфейса для вызова защищенного метода? - PullRequest
10 голосов
/ 11 ноября 2008

При просмотре исходного кода 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 . Тем не менее, я не уверен, почему автор, что таким образом.

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

Ответы [ 3 ]

14 голосов
/ 11 ноября 2008

Ну, не только для 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 it и использовать 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() { ... }
}

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

  • Bar.Clone ()
  • ICloneable.Clone ()

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

2 голосов
/ 06 января 2013

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

Наличие явной реализации интерфейса ничего не делает, кроме вызова защищенного метода, позволяет избежать этой проблемы, поскольку производный класс может изменить поведение метода интерфейса, переопределив виртуальный метод, сохранив при этом возможность вызывать базовую реализацию по своему усмотрению. , ИМХО, C # должен был иметь явную реализацию интерфейса для создания виртуального метода с CLS-совместимым именем, поэтому кто-то, пишущий на C #, является производным от класса, который явно реализовал 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, без необходимости отдельного виртуального метода.

1 голос
/ 23 августа 2012
  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
    }
}
...