В дефинициите на членовете в горните примери забелязваме употребата на запазената дума override. Без нея те не биха могли да бъдат компилирани. Това е така, защото въпросните членове са виртуални.
Виртуалните членове са един по-особен вид членове, без които полиморфизмът би бил неосъществим. Тяхната особеност проличава при наследяване – на наследяващите класове се дава възможност вместо изцяло да пресъздадат даден наследен виртуален метод, просто да предоставят своя имплементация на същия. Така, ако работим с обект от наследения клас през референция към базовия, той ще разполага с имплементациите, които наследникът е предоставил. Ще си изясним този механизъм при разглеждането на предефиниране и скриване на виртуални членове.
Виртуални членове се дефинират, като в дефиницията им се укаже ключовата дума virtual. Всички абстрактни членове, включително и тези, дефинирани в интерфейсите (и те са абстрактни, тъй като нямат имплементация), са винаги виртуални. Поради тази причина в някои обектно-ориентирани езици за програмиране (например в C++) абстрактните членове се наричат още "чисто виртуални".
Предефиниране и скриване
При дефиниране на виртуален член в тип-наследник, чиято сигнатура съвпада с член, дефиниран в някои от базовите типове, той може или да се предефинира (да му се даде нова имплементация) или да се "скрие".
Когато се използва ключовата дума override, се реализира предефиниране на виртуалния член, а когато се използва ключовата дума new – скриване, което е и опцията, която се подразбира когато не се укаже никаква ключова дума.
|
Когато в наследен клас се предефинира виртуален член на базовия, този член е виртуален и в наследения клас.
|
Най-лесно ще доловим разликата между скриването и предефинирането на членове, като първо обърнем внимание на следната модификация на по-горния пример. В нея вместо абстрактен сме използвали нормален, конкретен клас, който съдържа дефиниции на свойствата, връщащи подразбиращи се стойности и вместо override сме използвали new:
NonAbstractTest.cs
|
public class Car
{
public virtual int TopSpeed
{
// Retrieve the top speed in Kmph
get
{
return -1; // Default value
}
}
public virtual string BrandName
{
get
{
return "unknown"; // Default value
}
}
}
public class Trabant : Car
{
new public int TopSpeed
{
get
{
return 120;
}
}
new public string BrandName
{
get
{
return "Trabant";
}
}
}
public class Porsche : Car
{
new public int TopSpeed
{
get
{
return 250;
}
}
new public string BrandName
{
get
{
return "Porsche";
}
}
}
public class NonAbstractTest
{
static void Main()
{
Car[] cars = new Car[] {new Trabant(), new Porsche()};
foreach (Car car in cars)
{
Console.WriteLine("A {0} can go {1} Kmph",
car.BrandName, car.TopSpeed);
}
}
}
|
При изпълнението на този код получаваме следния, донякъде разочароващ, резултат:
A unknown can go -1 Kmph
A unknown can go -1 Kmph
|
Причината резултатът да се разминава с очакванията ни е, че при скриването на членовете наследяващият клас не предоставя своята дефиниция на базовия. Така, когато достъпваме обект от наследен клас през референция към обект от базовия, разполагаме само с неговите собствени реализации (на базовия клас). Поради това не можем да използваме полиморфизъм – когато достъпваме обект от базов клас, независимо от специфичният му тип, винаги ще ползваме имплементацията, дефинирана в базовия, т. е. той може приеме само една форма.
Трябва да отбележим, че ако пропуснем запазената дума new, поведението на кода ще бъде същото, но ще получим предупреждение от компилатора "The keyword new is required on '' because it hides inherited member".
Ако в горния пример заменим new с override, ще задействаме механизма на полиморфизма и резултатът ще бъде следния:
A Trabant can go 120 Kmph
A Porsche can go 250 Kmph
|
Ако в горния пример пропуснем да обявим членовете TopSpeed и BrandName като виртуални, ще получим същия разочароващ резултат, както и преди:
A unknown can go -1 Kmph
A unknown can go -1 Kmph
|
Виждаме, че при използването на полиморфизъм има много варианти да сбъркаме и да получим неправилно поведение. Затова можем да запомним следното правило:
|
За да действа полиморфизмът, трябва полиморфният метод в базовия тип да е виртуален (да е обявен като virtual, abstract или да е член на интерфейс) и в класа наследник да е имплементиран с override.
|
Сподели с приятели: |