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

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

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

Ответ 1

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

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

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

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

...

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

в конечном итоге вызовет Derived.SomeMethod, если это переопределяет Base.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();

Сначала вызовите Base.SomeOtherMethod, затем Derived.SomeOtherMethod. Они фактически представляют собой два совершенно разных метода, которые имеют одно и то же имя, а не производный метод, переопределяющий базовый метод.

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

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

Ответ 2

Любой метод может быть переопределяемым (= 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 экземпляром it Person.GetName, который вызывается:

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

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

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

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

Но иногда ваш друг Майк решает изменить свой пол и, таким образом, его имя Алисе:) Вы можете либо изменить исходный код, либо вместо этого подкласс 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 - вы вызываете Mike.GetName. Здесь не выполняется поиск во время выполнения - поскольку оба метода не являются виртуальными.

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

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

Ответ 3

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

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

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

A -> B -> C

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

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

Надеюсь, это поможет.

Ответ 4

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

   //This one is Simple method
public class Derived : Base
{
  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();
  • Использование virtual в базовом классе и переопределение в Derived даст D (полиморфизм).
  • Использование override без virtual в Base даст ошибку.
  • Аналогично, запись метода (без переопределения) с virtual будет записывать "B" с предупреждением (потому что никакой полиморфизм не выполняется).
  • Чтобы скрыть такое предупреждение, как указано выше, напишите new до этого простого метода в Derived.
  • new ключевое слово - это еще одна история, она просто скрывает предупреждение, указывающее, что свойство с таким же именем присутствует в базовом классе.
  • virtual или new оба одинаковы, кроме новый модификатор

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

  • sealed перед тем, как какой-либо класс или метод заблокируют его для использования в классе Derived, и он дает ошибку времени компиляции.