Почему явная реализация интерфейса? - PullRequest
9 голосов
/ 03 января 2009

Я недавно реализовал такой класс:

class TestClass : IDisposable
{
    RegistryKey m_key;
    public TestClass()
    {
        m_key = Registry.CurrentUser.OpenSubKey("Software", false);
    }

    public void Dispose()
    {
        // m_key.Dispose();
        IDisposable disp = m_key;
        disp.Dispose();
    }
}

Если я раскомментирую прямой вызов Dispose, я получаю ошибку CS0117 («Microsoft.Win32.RegistryKey» не содержит определения «Dispose»). Некоторый поиск в Google привел меня к этой теме , где я узнал, что происходит, и теперь я понимаю его механику. Документация MSDN предполагает, что автор предпочел бы, чтобы я вызывал Close () вместо Dispose (), но не объясняет почему.

Какова цель этого паттерна (который, я думаю, я видел в классах IO)? В свете того факта, что это было преднамеренное решение автора класса, насколько плох код выше (вызов Dispose через интерфейс IDisposable)? Это не может быть слишком плохо - в конце концов, это то, что произойдет в операторе использования, верно?

[правит: 1) изменил заголовок с "непубличного" на "явный" 2) удалил явную реализацию из моего кода, случайно оставленную в эксперименте]

Ответы [ 2 ]

15 голосов
/ 03 января 2009

Это называется явная реализация интерфейса . В вашем примере, поскольку вы определяете метод Dispose () как «void IDisposable.Dispose ()», вы также явно реализуете интерфейс IDisposable.

Обычно это делается для того, чтобы избежать столкновений. Если бы Microsoft когда-либо захотела добавить другой метод Dispose (), который сделал бы что-то еще с RegistryKey, они бы не смогли, если бы не использовали явную реализацию этого интерфейса.

Это часто делается с помощью универсального интерфейса IEnumerable . Требуется также реализовать неуниверсальный интерфейс IEnumerable. Единственный член в этих двух интерфейсах - это GetEnumerator, более универсальный из которых более полезен, поэтому его обычно реализуют так:

public clas SomeClass : IEnumerable<SomeOtherClass>
{
    public IEnumerator<SomeOtherClass> GetEnumerator ()
    {
        ...
    }

    IEnumerator IEnumerable.GetEnumerator ()
    {
        return GetEnumerator ();
    }
}

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

См. Страницы 166-169 из Программирование на C # Джесси Либерти (у меня четвертое издание).

0 голосов
/ 03 января 2009

Большинство людей не согласны со мной, но Мне нравится использовать явную реализацию интерфейса для всех интерфейсов . Я хочу прояснить, пишу ли я метод, который будет вызываться на моем объекте или на моем интерфейсе.

Это больно, если у вас есть ссылка на объект и вы хотите вызвать метод интерфейса (как в приведенном выше примере), но я уменьшу его, написав:

class C : IDisposable
{
    public IDisposable IDisposable { get { return this; } }
    void IDisposable.Dispose() { }
}

, что означает, что вызов метода на C выглядит следующим образом:

C c = ...
c.IDisposable.Dispose();

Компилятор анализирует это как «вызов свойства IDisposable для C, затем вызов метода Dispose() для результата», но я читаю его как «вызов метода IDisposable.Dispose() для C», что здесь естественно .

К сожалению, такой подход может быть уродливым при использовании универсальных интерфейсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...