Переопределение против метода скрывается [дубликат

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

  •  27-09-2019
  •  | 
  •  

Вопрос

Этот вопрос уже имеет ответ здесь:

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

Я запутался в переопределении - почему мы переопределяем? До сих пор я узнал, что путем переоценения мы можем предоставить желаемую реализацию методу производного класса, не изменив подпись.

Если я не переопределяю метод суперкласса, и я вносим изменения в метод в подраздельном классе, это вносит изменения в метод супер класса?

Я также смущает следующее - что это делает?

class A
{
    virtual m1()
    {
        console.writeline("Bye to all");
    }
}

class B : A
{
    override m1()
    {
        console.writeLine("Hi to all");
    }
}

class C
{
    A a = new A();
    B b = new B();
    a = b; (what is this)
    a.m1(); // what this will print and why?

    b = a; // what happens here?
}
Это было полезно?

Решение

Рассмотреть возможность:

public class BaseClass
{
  public void WriteNum()
  {
    Console.WriteLine(12);
  }
  public virtual void WriteStr()
  {
    Console.WriteLine("abc");
  }
}

public class DerivedClass : BaseClass
{
  public new void WriteNum()
  {
    Console.WriteLine(42);
  }
  public override void WriteStr()
  {
    Console.WriteLine("xyz");
  }
}
/* ... */
BaseClass isReallyBase = new BaseClass();
BaseClass isReallyDerived = new DerivedClass();
DerivedClass isClearlyDerived = new DerivedClass();

isReallyBase.WriteNum(); // writes 12
isReallyBase.WriteStr(); // writes abc
isReallyDerived.WriteNum(); // writes 12
isReallyDerived.WriteStr(); // writes xyz
isClearlyDerived.WriteNum(); // writes 42
isClearlyDerived.writeStr(); // writes xyz

Переопределение является классическим способом OO, в котором полученный класс может иметь более конкретное поведение, чем базовый класс (на некоторых языках у вас нет выбора, кроме как для этого). Когда на объекте вызывается виртуальный метод, то называется наиболее полученной версией метода. Следовательно, хотя мы имеем дело с isReallyDerived как BaseClass Затем функциональность определена в DerivedClass используется.

Скрытие означает, что у нас есть совершенно другой метод. Когда мы называем WriteNum() на isReallyDerived Тогда нет никакого способа узнать, что есть другой WriteNum() на DerivedClass Так что это не вызывается. Это можно назвать только тогда, когда мы имеем дело с объектом так как а. DerivedClass.

Большую часть времени прятается плохо. Как правило, либо вы должны иметь метод как виртуальный, если его могут быть изменены в полученном классе, и переопределить его в полученном классе. Однако есть две вещи полезны для:

  1. Передняя совместимость. Если DerivedClass был А. DoStuff() метод, а потом позже на BaseClass был изменен на добавление DoStuff() метод (помните, что они могут быть написаны разными людьми и существуют в разных собраниях), тогда запрет на сокрытие члена внезапно сделал DerivedClass багги без него меняется. Кроме того, если новый DoStuff() на BaseClass был виртуальным, то автоматически делая это на DerivedClass Переопределение его может привести к ранее существующему методу, называемую, когда он не должен. Следовательно, хорошо, что скрытие по умолчанию (мы используем new Чтобы понять, что мы определенно хотели скрыть, но оставляя его скрытыми и излучают предупреждение о компиляции).

  2. Бедняк ковариации. Рассмотреть а Clone() метод включения BaseClass это возвращает новый BaseClass Это копия созданного. Во время переопределения на DerivedClass Это создаст DerivedClass но верните его как BaseClass, который не так полезно. Что мы могли бы сделать, это иметь виртуальный защищенный CreateClone() это переопределено. В BaseClass у нас есть Clone() это возвращает результат этого - и все хорошо - в DerivedClass Мы скрываем это с новым Clone() Это возвращает а DerivedClass. Отказ Призыв Clone() на BaseClass всегда будет возвращать BaseClass Ссылка, которая будет BaseClass ценность или а DerivedClass ценность в зависимости от ситуации. Призыв Clone() на DerivedClass вернется А. DerivedClass Значение, которое мы хотели бы в этом контексте. Есть другие варианты этого принципа, однако следует отметить, что все они довольно редко.

Важное, что следует отметить со вторым случаем, заключается в том, что мы использовали скрытие именно к Удалить сюрпризы к звонкому коду, как человек, использующий DerivedClass может разумно ожидать его Clone() вернуть А. DerivedClass. Отказ Результаты любого из способов можно назвать, поддерживают друг друга. Большинство случаев скрытия риска, вводящих сюрпризы, поэтому они обычно нахмуриваются. Этот оправдан точно, потому что он решает самую проблему, которая часто скрывается.

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

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

Переопределение - это когда вы предоставляете новый override Реализация метода в классе потомка, когда этот метод определяется в базовом классе, как virtual.

Скрытие, когда вы предоставляете новую реализацию метода в классе потомка, когда этот метод нет определяется в базовом классе как virtual, или когда ваша новая реализация не указывает override.

Скрытие очень часто плохо; Как правило, вы должны попробовать не делать этого, если вы можете избежать его вообще. Скрытие может привести к неожиданным вещам, потому что скрытые методы используются только при вызове в переменной фактического типа, который вы определены, а не если использование ссылки на базовый класс ... с другой стороны, виртуальные методы, которые переоценены Правильный метод версии вызывается, даже при вызове, используя ссылку на базовый класс на детский класс.

Например, рассмотрим эти классы:

public class BaseClass
{
  public virtual void Method1()  //Virtual method
  {
    Console.WriteLine("Running BaseClass Method1");
  }
  public void Method2()  //Not a virtual method
  {
    Console.WriteLine("Running BaseClass Method2");
  }
}
public class InheritedClass : BaseClass
{
  public override void Method1()  //Overriding the base virtual method.
  {
    Console.WriteLine("Running InheritedClass Method1");
  }
  public new void Method2()  //Can't override the base method; must 'new' it.
  {
    Console.WriteLine("Running InheritedClass Method2");
  }
}

Давайте назовем это так, с экземпляром унаследованногоClass, в соответствии с сопоставлением:

InheritedClass inherited = new InheritedClass();
inherited.Method1();
inherited.Method2();

Это возвращает то, что вы должны ожидать; Оба метода говорят, что они управляют версиями наследства.

Бег унаследованного метода Class1.
Бег унаследованного Class Method2.

Этот код создает экземпляр того же, унаследованногоClass, но хранит его в ссылке BaseClass:

BaseClass baseRef = new InheritedClass();
baseRef.Method1();
baseRef.Method2();

Обычно при принципах ООП вы должны ожидать тот же выход, что и вышеприведенный пример. Но вы не получаете тот же выход:

Бег унаследованного метода Class1.
Бег Baseclass Method2.

Когда вы написали код унаследованногоClass, вы, возможно, хотели, чтобы все звонки Method2() Чтобы запустить код, который вы написали в нем. Обычно это было бы, как это работает - предполагая, что вы работаете с virtual Метод, который вы переопределили. Но потому что вы используете new/ Скрытый метод, он вызывает версию на ссылку, которую вы используете, вместо этого.


Если это поведение, которое вы Действительно хочу, тогда; Там вы идете. Но я решительно предположил, что если это то, что вы хотите, с кодом может быть большая архитектурная проблема.

Переопределение метода является простой переопределением реализации по умолчанию методом базового класса в полученном классе.

Скрытие метода: вы можете использовать «новое» ключевое слово перед виртуальным методом в полученном классе

так как

class Foo  
{  
  public virtual void foo1()  
  {  

  }  
}  

class Bar:Foo  
{  
  public new virtual void foo1()  
  {   

  }  
}  

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

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