Почему мы используем виртуальные и переопределяем?
Почему мы используем override и virtual, если он дает тот же эффект, когда мы не используем переопределение и виртуальные?
пример 1:
class BaseClass
{
public virtual string call()
{
return "A";
}
}
class DerivedClass : BaseClass
{
public override string call()
{
return "B";
}
}
вывод: B
Пример 2:
class BaseClass
{
public string call()
{
return "A";
}
}
class DerivedClass : BaseClass
{
public string call()
{
return "B";
}
}
и вывод все тот же:
вывод: B
для запуска теста:
class Program
{
static void Main(string[] args)
{
DerivedClass dc = new DerivedClass();
Console.WriteLine(dc.call());
Console.ReadKey();
}
}
Является ли компилятор автоматически добавляться и переопределяться во время компиляции?
Я был бы рад, если бы кто-нибудь объяснил мне причину использования виртуальных и переопределить.
Ответы
Ответ 1
(заметьте, я спокойно игнорирую ошибки компиляции)
Теперь выполните:
BaseClass obj = new DerivedClass();
Console.WriteLine(obj.call());
Без virtual
, это напечатает A
, когда на самом деле a DerivedClass
должен писать B
. Это связано с тем, что он просто вызвал реализацию BaseClass
(поскольку obj
набирается как BaseClass
, и никакой полиморфизм не определен).
Ответ 2
Виртуальное и переопределение являются базовым механизмом наследования в объектно-ориентированном программировании.
Это, пожалуй, самое важное для понимания, когда вы используете классы на языке С# или Java.
http://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)
Наследование позволяет повторно использовать код, добавляющий новые поля, свойства и методы или заменяя методы и свойства ранее определенных классов.
Virtual и Override позволяют вам заменять содержимое метода, и когда я говорю "replace", я говорю "replace".
Я бы предложил вам хороший пример.
public class MyClassEnglish
{
public virtual string SomethingToSay()
{
return "Hello!";
}
public void WriteToConsole()
{
Console.WriteLine(this.SomethingToSay());
}
}
public class MyClassItalian :
MyClassEnglish
{
public override string SomethingToSay()
{
return "Ciao!";
}
}
int main()
{
MyClassItalian it = new MyClassItalian();
it.WriteToConsole();
}
Если вы опустите виртуальный и переопределите, MyClassItalian выведет "Hello!" а не "Цяо!".
В вашем примере вы показываете технику Shadowing, но компилятор должен дать вам предупреждение.
Вы хотите добавить новое ключевое слово, если хотите скрыть метод в базовом классе.
Скрытие метода не переопределяет! Просто скрывается.
Одно из возможных применений, которое приходит мне в голову, заключается в том, что его можно использовать, когда вам нужна какая-то оптимизация, например.
public abstract class MySpecialListBase
{
public int Count()
{
return this.GetCount();
}
protected abstract int GetCount();
}
public sealed class MySpecialArrayList : MySpecialListBase
{
int count;
public new int Count()
{
return this.count;
}
protected override int GetCount()
{
return this.count;
}
}
Теперь...
Вы можете использовать MySpecialListBase во всем своем коде, а при вызове Count() он вызовет виртуальный метод GetCount().
Но если вы используете только MySpecialArrayList, он вызовет оптимизированный Count(), который не является виртуальным, и который просто возвращает поле, увеличивая производительность.
// This works with all kind of lists, but since it is a more general purpose method it will call the virtual method.
public void MyMethod(MySpecialListBase list)
{
Console.WriteLine(list.Count());
}
// This works only with MySpecialArrayList, and will use the optimized method.
public void MyMethod(MySpecialArrayList list)
{
Console.WriteLine(list.Count());
}
Ответ 3
Лучший пример: я могу придумать, где это полезно, когда вы создаете свой собственный объект (класс), и вам нужно добавить список этого объекта в поле со списком.
Когда вы добавляете свой объект в combobox, вы хотите иметь возможность контролировать, какой текст отображается для каждого элемента. Object.toString - виртуальный метод. http://msdn.microsoft.com/en-us/library/system.object.tostring.aspx, и из-за этого вы можете переопределить этот метод и установить .toString, чтобы отобразить правильную информацию о вашем объекте, переопределив его.
public MyClass()
{
private int ID;
public override string ToString()
{
return "My Item:" + ID;
}
}
Ответ 4
Переопределение метода:
Где вы определяете или реализуете виртуальный метод в родительском классе, а затем заменяете его в классе потомков.
Когда вы решите объявить метод как виртуальный, вы даете разрешение производным классам расширять и переопределять метод с их собственной реализацией. Вы можете также использовать расширенный метод для кода родительского метода.
В большинстве языков OO вы также можете скрыть родительский метод. Когда вы вводите новую реализацию одного и того же метода с одной и той же сигнатурой без переопределения, вы скрываете родительский метод.
С# Переопределение
В С# вы указываете виртуальный метод с ключевым словом virtual в родительском классе и расширяете (или заменяете) его в классе потомков, используя ключевое слово переопределения.
Используйте ключевое слово base в методе потомка для выполнения кода в родительском методе, т.е. base.SomeMethod().
Пример синтаксиса:
class Robot
{
public virtual void Speak()
{
}
}
class Cyborg:Robot
{
public override void Speak()
{
}
}
Сведения о переопределении
Вы не можете переопределить обычный не виртуальный метод, а также статический метод.
Первая версия родительского метода должна быть виртуальной или абстрактной.
Вы можете переопределить любой родительский метод, помеченный как виртуальный, абстрактный, так и переопределенный (уже переопределенный).
Методы должны иметь одну и ту же подпись.
Методы должны иметь одинаковую видимость (одинаковый уровень доступа).
Используйте ключевое слово base для обращения к родительскому классу, как в base.SomeMethod().
Пример переопределения С#
Следующий фрагмент кода демонстрирует использование virtual и override для переопределения родительского метода в классе потомков.
using System;
class Dog
{
public virtual void Bark()
{
Console.WriteLine("RUFF!");
}
}
class GermanShepard:Dog
{
public override void Bark()
{
Console.WriteLine("Rrrrooouuff!!");
}
}
class Chiuaua:Dog
{
public override void Bark()
{
Console.WriteLine("ruff");
}
}
class InclusionExample
{
public static void Main()
{
Dog MyDog=new Dog();
MyDog=new GermanShepard();
MyDog.Bark(); // prints Rrrrooouuff!!
MyDog=new Chiuaua();
MyDog.Bark(); // prints ruff;
}
}
Скрытие метода с помощью нового
Используйте новое ключевое слово, чтобы ввести новую реализацию родительского метода (это скрывает родительский метод). Вы можете скрыть метод без использования нового, но вы получите предупреждение о компиляторе. Использование нового будет подавлять предупреждение.
Новые и переопределяющие модификаторы имеют разные значения. Новый модификатор создает новый элемент с тем же именем, подписью и видимостью и скрывает исходный элемент. Модификатор переопределения расширяет реализацию для унаследованного элемента и позволяет реализовать полиморфизм на основе наследования.
Избегайте появления новых членов: иногда есть явные причины для внедрения нового метода с тем же именем, сигнатурой и видимостью родительского метода. В этих очевидных случаях введение нового участника является мощной функцией. Однако, если у вас нет ясной причины, то не следует вводить новую версию метода, называя новый метод чем-то уникальным и подходящим.
class Robot : System.Object
{
public void Speak()
{
MessageBox.Show("Robot says hi");
}
}
class Cyborg : Robot
{
new public void Speak()
{
MessageBox.Show("hi");
}
}
Вызов базовой версии класса
Общая задача. В OO необходимо расширить метод, сначала выполнив код родительского метода, а затем добавив код. Используйте ключевое слово base, чтобы ссылаться на родительский класс как на base.SomeMethod().
class Robot : System.Object
{
public virtual void Speak()
{
MessageBox.Show("Robot says hi");
}
}
class Cyborg : Robot
{
public override void Speak()
{
base.Speak();
MessageBox.Show("hi");
}
}