Разница между виртуальным, переопределением, новым и закрытым переопределением
Я довольно смущен между некоторыми понятиями ООП: 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, и он дает ошибку времени компиляции.