Зачем нужна явная реализация интерфейса?

StackOverflow https://stackoverflow.com/questions/408415

  •  03-07-2019
  •  | 
  •  

Вопрос

Недавно я реализовал такой класс, как:

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(), но не объясняет почему.

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

Другие советы

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

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

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

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

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

Компилятор анализирует это как "вызовите IDisposable свойство на C, затем вызовите Dispose() метод по результату", но я прочитал это как "Вызовите IDisposable.Dispose() способ включения C- что здесь кажется естественным.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top