Как «правильно» переопределить метод базового класса? - PullRequest
35 голосов
/ 01 июля 2010

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

1) Вызовите base.Method (), а затем предоставьте мою реализацию.

2) Укажите мою реализацию и затем вызовите base.Method ()

3) Просто предоставьте мою реализацию.

Недавно, используя библиотеку, я обнаружил несколько ошибок, которые были внесены из-за нереализации метода, как ожидалось библиотекой. Я не уверен, что это плохо для библиотеки или что-то не так в моем понимании.

Я приведу один пример.

public class ViewManager {
     public virtual void Customize(){
        PrepareBaseView();
     }
}

public class PostViewManager {
     public override void Customize(){
        base.Customize();
        PreparePostView();
     }
}


public class PreViewManager {
     public override void Customize(){
        PreparePreView();
        base.Customize();
     }
}


public class CustomViewManager {
     public override void Customize(){
        PrepareCustomView();
     }
}

Мой вопрос здесь заключается в том, как может дочерний класс узнать (не глядя на реализацию базового класса), какой порядок (или параметр) ожидается родительским классом? Есть ли способ, которым родительский класс мог бы принудительно применить один из трех альтернатив всем производным классам?

Ответы [ 4 ]

39 голосов
/ 01 июля 2010

как дочерний класс мог знать (не глядя на реализацию базового класса), какой порядок (или опция) ожидается родительским классом?

Нет способа «узнать» это, когда вы создаете подкласс и переопределяете метод.Правильная документация на самом деле является единственным вариантом здесь.

Есть ли способ, которым родительский класс мог бы применить один из трех альтернатив всем производным классам?

ЕдинственныйВариант здесь заключается в том, чтобы избежать проблемы.Вместо того, чтобы позволить подклассу переопределять метод, он может быть объявлен не виртуальным и вызывать виртуальный метод в соответствующем месте.Например, если вы хотите, чтобы эти подклассы «сначала вызывали вашу версию», вы могли бы сделать:

public class BaseClass {
    public void Method() // Non-virtual
    {
          // Do required work

          // Call virtual method now...
          this.OnMethod();
    }

    protected virtual void OnMethod()
    { // Do nothing
    }
 }

Подклассы могут затем «переопределить» OnMethod и предоставить функциональность, которая происходит после «метода»работа.

Причина, по которой это требуется, заключается в том, что виртуальные методы предназначены для того, чтобы позволить подклассу полностью заменить реализацию родительского класса.Это сделано специально.Если вы хотите предотвратить это, лучше сделать метод не виртуальным.

2 голосов
/ 01 июля 2010

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

При написании кода я всегда устал следоватьПравило, которое гласит:

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

Это взято из http://msdn.microsoft.com/en-us/library/ms229011.aspx,, однако это для разработки событий, хотя я считаю, что я прочитал это в книге Руководства по проектированию платформы(http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321246756).

Однако это, очевидно, неверно, например, для веб-форм ASP.NET требуется базовый вызов Page_Load.

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

1 голос
/ 01 июля 2010

Короткий ответ - нет.Вы не можете принудительно установить, в каком порядке дочерний объект вызывает базовый метод или вообще вызывает его.

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

1) Создать не виртуальную функцию в базовом классе.Давайте назовем это MyFunction

2) Создайте защищенную виртуальную функцию в базовом классе.Давайте назовем это _MyFunction

3) Производные классы расширяют метод _MyFunction.

4) Пусть MyFunction вызывает _MyFunction и запускает код, необходимый для запуска до или после вызова.

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

0 голосов
/ 01 июля 2010

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

...