Разница между виртуальным, переопределенным, новым и запечатанным переопределением


Я довольно запутался между некоторыми понятиями ООП:virtual,override,new и sealed override. Кто-нибудь может объяснить разницу?

Я довольно ясно, что если метод производного класса должен использоваться, можно использовать override ключевое слово, чтобы метод базового класса был переопределен производным классом. Но я не уверен насчет new и sealed override.

4 67

4 ответа:

The виртуальный ключевое слово используется для изменения метода, свойства, индексатора или объявления события и позволяет переопределить его в производном классе. Например, этот метод может быть переопределен любым производным классом: Используйте новый модификатор, чтобы явно скрыть элемент, унаследованный от базового класса. Чтобы скрыть унаследованный член, объявите его в производном классе с тем же именем и измените его с помощью нового модификатора.

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

public class Base
{
  public virtual void SomeMethod()
  {
  }
}

public class Derived : Base
{
  public override void SomeMethod()
  {
  }
}

...

Base d = new Derived();
d.SomeMethod();

в конечном итоге вызов производного.SomeMethod, если это переопределяет базу.Метода someMethod.

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

public class Base
{
  public virtual void SomeOtherMethod()
  {
  }
}

public class Derived : Base
{
  public new void SomeOtherMethod()
  {
  }
}

...


Base b = new Derived();
Derived d = new Derived();
b.SomeOtherMethod();
d.SomeOtherMethod();

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

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

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

любой метод может быть переопределяемым (=virtual) или нет. Решение принимает тот, кто определяет способ:

class Person
{
    // this one is not overridable (not virtual)
    public String GetPersonType()
    {
        return "person";
    }

    // this one is overridable (virtual)
    public virtual String GetName()
    {
        return "generic name";
    }
}

теперь вы можете переопределить те методы, которые переопределяются:

class Friend : Person
{
    public Friend() : this("generic name") { }

    public Friend(String name)
    {
        this._name = name;
    }

    // override Person.GetName:
    public override String GetName()
    {
        return _name;
    }
}

но вы не можете переопределить GetPersonType способ, потому что он не виртуальный.

давайте создадим два экземпляра этих классов:

Person person = new Person();
Friend friend = new Friend("Onotole");

когда невиртуальный метод GetPersonType называется Fiend экземпляра это на самом деле Person.GetPersonType это звонил:

Console.WriteLine(friend.GetPersonType()); // "person"

когда виртуальный метод GetName называется Friend пример Friend.GetName это называется:

Console.WriteLine(friend.GetName()); // "Onotole"

когда виртуальный метод GetName называется Person пример Person.GetName это называется:

Console.WriteLine(person.GetName()); // "generic name"

когда вызывается невиртуальный метод, тело метода не просматривается-компилятор уже знает фактический метод, который должен быть вызван. В то время как с виртуальными методами компилятор не может быть уверен, какой из них вызвать, и это посмотрел вверх во время выполнения в иерархии классов снизу вверх, начиная с типа экземпляра, на который вызывается метод: for friend.GetName это выглядит, начиная с Friend класс и находит его сразу, за person.GetName класс начинается с Person и находит его там.

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

class Mike : Friend
{
    public sealed override String GetName()
    {
        return "Mike";
    }
}

но иногда ваш друг Майк решает изменить свой пол и, таким образом, его имя на Алису :) вы можете либо изменить исходный код, либо вместо этого подкласс Майк:

class Alice : Mike
{
    public new String GetName()
    {
        return "Alice";
    }
}

здесь вы можете создать совершенно другой метод с тем же именем (теперь у вас два). Какой метод и когда вызывается? Это зависит от того, как вы это называете:

Alice alice = new Alice();
Console.WriteLine(alice.GetName());             // the new method is called, printing "Alice"
Console.WriteLine(((Mike)alice).GetName());     // the method hidden by new is called, printing "Mike"

когда вы называете его из Aliceперспективу вы называете Alice.GetName, когда из Mike ' s-вы звоните Mike.GetName. Здесь не выполняется поиск во время выполнения, поскольку оба метода не являются виртуальными.

вы всегда можете создать new методы - независимо от того, являются ли методы, которые вы скрываете, виртуальными или нет.

это также относится к свойствам и событиям - они представлены в виде методов внизу.

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

исключением из вышесказанного является new модификатор. Метод не объявлен virtual или abstract можно переопределить с помощью new модификатор в производном классе. При вызове метода в базовом классе выполняется базовый метод, а при вызове в производном классе выполняется новый метод. Все это new ключевые слова позволяет сделать это двумя способами:С тем же именем в иерархии классов.

наконец-то a sealed модификатор разрывает цепи virtual методы и делает их не переопределяемый снова. Это используется не часто, но вариант быть там. Это имеет больше смысла с цепочкой из 3 классов, каждый из которых происходит от предыдущего

A -> B -> C

если A есть virtual или abstract метод, то есть overridden на B, то он также может предотвратить C от изменения его снова, объявив его sealed in B.

sealed также используется в classes, и именно там вы обычно сталкиваетесь с этим ключевым словом.

я надеюсь, что это помогает.

 public class Base 
 {
   public virtual void SomeMethod()
   {
     Console.WriteLine("B");
   }
  }

public class Derived : Base
{
   //Same method is written 3 times with different keywords to explain different behaviors. 


   //This one is Simple method
  public void SomeMethod()
  {
     Console.WriteLine("D");
  }

  //This method has 'new' keyword
  public new void SomeMethod()
  {
     Console.WriteLine("D");
  }

  //This method has 'override' keyword
  public override void SomeMethod()
  {
     Console.WriteLine("D");
  }
}

теперь первым делом

 Base b=new Base();
 Derived d=new Derived();
 b.SomeMethod(); will always write B
 d.SomeMethod(); will always write D
ключевые слова Все о полиморфизме
                              Base b = new Derived();
  1. используя virtual в базовом классе и переопределить в Derived даст D (полиморфизм).
  2. используя override без virtual на Base выдаст ошибку.
  3. аналогично писать метод (без переопределения) с virtual будет писать ' B ' с предупреждением (потому что полиморфизм не делается).
  4. чтобы скрыть такое предупреждение, как в выше точки пишите new перед этим простой метод в Derived.
  5. new ключевое слово-это другая история, оно просто скрывает предупреждение, которое говорит, что свойство с тем же именем находится в базовом классе.
  6. virtual или new оба одинаковы, за исключением новый модификатор

  7. new и override нельзя использовать перед тем же методом или свойством.

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