Разница между виртуальным, переопределенным, новым и запечатанным переопределением - PullRequest
75 голосов
/ 28 мая 2011

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

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

Ответы [ 4 ]

100 голосов
/ 28 мая 2011

Ключевое слово 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.

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

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. По сути, это два совершенно отдельных метода, которые имеют одно и то же имя, а не производный метод, переопределяющий базовый метод.

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

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

35 голосов
/ 28 мая 2011

Любой метод может быть переопределен (= 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"

Когда вызывается не виртуальный метод, тело метода не ищется - компилятор уже знает фактический метод, который должен быть вызван. Принимая во внимание, что с помощью виртуальных методов компилятор не может быть уверен, какой из них вызывать, и он просматривается во время выполнения в иерархии классов снизу вверх, начиная с типа экземпляра, для которого вызывается метод: для 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 - вы звоните Mike.GetName. Здесь не выполняется поиск во время выполнения, поскольку оба метода не виртуальны.

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

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

17 голосов
/ 28 мая 2011

По умолчанию метод не может быть переопределен в производном классе, если он не объявлен 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, и именно здесь вы часто встречаетесь с этим ключевым словом.

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

8 голосов
/ 10 июля 2014
 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 перед тем, как какой-либо класс или метод заблокирует его дляиспользуется в классе Derived и выдает ошибку времени компиляции.
...