Basis- en afgeleide klassen configureren
De termen basisklasse en afgeleide klasse worden gebruikt om de relatie tussen klassen in een overnamehiërarchie te beschrijven. Een basisklasse wordt gebruikt om een algemene set kenmerken en gedragingen te definiëren die andere klassen overnemen. Een afgeleide klasse wordt gebruikt om een gespecialiseerde set kenmerken en gedragingen te definiëren waarmee de basisklasse wordt uitgebreid of gewijzigd.
Transitieve aard van erfenis onderzoeken
In C# kan een klasse slechts overnemen van één basisklasse. Deze beperking staat bekend als enkele overerving. Met één overname kan een afgeleide klasse toegang krijgen tot de eigenschappen en methoden van één basisklasse, waardoor een duidelijke en eenvoudige hiërarchie wordt bevorderd. Het concept van meerdere overerving wordt niet ondersteund in C#. Deze beperking voorkomt de complexiteit en dubbelzinnigheden die kunnen optreden bij het overnemen van meerdere basisklassen. In plaats daarvan gebruikt C# interfaces om vergelijkbare functionaliteit te bereiken.
Hoewel een klasse kan overnemen van slechts één basisklasse, is overname transitief. Dit betekent dat als een klasse C erft van klasse B en klasse B erft van klasse A, klasse C de leden erft die zijn gedeclareerd in zowel klasse B als klasse A. Deze eigenschap maakt diepere hiërarchieën en hergebruik van code mogelijk.
Denk bijvoorbeeld aan een voertuigbeheersysteem waarbij u een basisklasse hebt Vehicle, een afgeleide klasse Car die overgaat van Vehicleen een andere afgeleide klasse ElectricCar die overgaat van Car. In deze hiërarchie neemt ElectricCar leden over van zowel Car als Vehicle, waarmee de transitieve aard van de overname wordt gedemonstreerd.
public class Vehicle
{
public string Make { get; set; }
public string Model { get; set; }
public void StartEngine()
{
Console.WriteLine("Engine started.");
}
public void StopEngine()
{
Console.WriteLine("Engine stopped.");
}
}
public class Car : Vehicle
{
public int NumberOfDoors { get; set; }
public void OpenTrunk()
{
Console.WriteLine("Trunk opened.");
}
public void HonkHorn()
{
Console.WriteLine("Horn honked.");
}
public void LockDoors()
{
Console.WriteLine("Doors locked.");
}
}
public class ElectricCar : Car
{
public int BatteryCapacity { get; set; }
public void ChargeBattery()
{
Console.WriteLine("Battery charging.");
}
public void DisplayBatteryStatus()
{
Console.WriteLine("Battery status displayed.");
}
}
public class CombustionEngineCar : Car
{
public int FuelCapacity { get; set; }
public void Refuel()
{
Console.WriteLine("Car refueled.");
}
public void CheckOilLevel()
{
Console.WriteLine("Oil level checked.");
}
}
In dit voorbeeld ziet u een eenvoudig voertuigbeheersysteem met de volgende klassen:
Vehicleklasse: De basisklasseVehiclebevat algemene eigenschappen en methoden voor alle voertuigen, zoals Make, Model, StartEngine en StopEngine.Carklasse: De klasseCarneemt over vanVehicleen voegt specifieke eigenschappen en methoden toe voor auto's, zoalsNumberOfDoors,OpenTrunk,HonkHornenLockDoors.ElectricCarklasse: De klasseElectricCarneemt over vanCaren voegt eigenschappen en methoden toe die specifiek zijn voor elektrische auto's, zoalsBatteryCapacity,ChargeBatteryenDisplayBatteryStatus.CombustionEngineCarklasse: DeCombustionEngineCarklasse neemt ook over vanCaren voegt eigenschappen en methoden toe die specifiek zijn voor verbrandingsmotorauto's, zoalsFuelCapacity,RefuelenCheckOilLevel.
In dit voorbeeld neemt de Car klasse leden over van de Vehicle-klasse en nemen de ElectricCar en CombustionEngineCar leden over van de Car-klasse. Deze hiërarchie toont de transitieve aard van overname, waarbij ElectricCar en CombustionEngineCar leden overnemen van zowel Car als Vehicle.
Erfelijkheid en zichtbaarheid van leden onderzoeken
Wanneer een klasse wordt overgenomen van een basisklasse, zijn de volgende regels van toepassing:
- Statische constructors en instantieconstructors worden niet overgenomen.
- Alle andere leden van de basisklasse worden overgenomen, maar toegangsaanpassingen zijn van invloed op hun zichtbaarheid in de afgeleide klasse. Toegangsaanpassingen zijn onder andere:
public,protected,internalenprivate.
Openbare leden
Openbare leden zijn toegankelijk vanuit elke code die toegang heeft tot de klasse. Afgeleide klassen nemen openbare leden over en ze zijn toegankelijk van buiten de klassehiërarchie.
public class BaseClass
{
public int publicField;
public void PublicMethod() { }
}
public class DerivedClass : BaseClass
{
public void AccessPublicMember()
{
publicField = 10;
PublicMethod();
}
}
In dit voorbeeld neemt de DerivedClass de publicField en PublicMethod leden over van de BaseClass. De methode AccessPublicMember in de DerivedClass heeft toegang tot deze leden. Openbare leden zijn ook toegankelijk vanuit code die zich buiten de klassehiërarchie bevindt.
Beveiligde leden
Beveiligde leden zijn toegankelijk binnen de klasse waarin ze worden gedeclareerd en binnen afgeleide klassen. Ze zijn niet toegankelijk van buiten de klassehiërarchie.
public class BaseClass
{
protected int protectedField;
protected void ProtectedMethod() { }
}
public class DerivedClass : BaseClass
{
public void AccessProtectedMember()
{
protectedField = 10;
ProtectedMethod();
}
}
In dit voorbeeld neemt de DerivedClass de protectedField en ProtectedMethod leden over van de BaseClass. De methode AccessProtectedMember in de DerivedClass heeft toegang tot deze leden. Als u echter toegang probeert te krijgen tot beveiligde leden van buiten de klassehiërarchie, wordt er een compileertijdfout gegenereerd.
Interne leden
Interne leden zijn toegankelijk binnen dezelfde assemblage. Ze zijn niet toegankelijk van buiten de assembly, zelfs niet als de klasse wordt overgenomen.
public class BaseClass
{
internal int internalField;
internal void InternalMethod() { }
}
public class DerivedClass : BaseClass
{
public void AccessInternalMember()
{
internalField = 10;
InternalMethod();
}
}
In dit voorbeeld neemt de DerivedClass de internalField en InternalMethod leden over van de BaseClass. De AccessInternalMember methode in de DerivedClass heeft toegang tot deze leden omdat ze zich in dezelfde assembly bevinden. Als u echter toegang probeert te krijgen tot interne leden van buiten de assembly, wordt er een compilatiefout gegenereerd.
Privéleden
Privéleden zijn alleen toegankelijk binnen de klas waarin ze worden gedeclareerd. Afgeleide klassen nemen geen private leden over en zijn daarom niet direct toegankelijk in de afgeleide klasse.
public class BaseClass
{
private int privateField;
private void PrivateMethod() { }
}
public class DerivedClass : BaseClass
{
public void AccessPrivateMember()
{
// Can't access privateField or PrivateMethod
}
}
In dit voorbeeld neemt de DerivedClass over van de BaseClass, maar heeft deze geen toegang tot de privateField of PrivateMethod leden omdat ze privé zijn.
Het gebruik van abstracte, virtuele en verzegelde trefwoorden in de basisklasse onderzoeken
Basis- en afgeleide klassen gebruiken de abstract, virtualen sealed trefwoorden om het gedrag te bepalen van leden die worden overgenomen. Met deze trefwoorden kunt u het controleniveau definiëren dat afgeleide klassen hebben voor de leden van de basisklasse.
Het gebruik van het trefwoord abstract onderzoeken
Het abstract trefwoord in C# wordt gebruikt om klassen en klasseleden te definiëren die onvolledig zijn en moeten worden geïmplementeerd in afgeleide klassen. Een abstracte klasse kan niet rechtstreeks worden geïnstantieerd en is bedoeld als basisklasse voor andere klassen. Abstracte methoden en eigenschappen worden zonder implementatie gedeclareerd en moeten worden overschreven in niet-abstracte afgeleide klassen.
Bijvoorbeeld:
public abstract class Shape
{
public abstract int GetArea();
}
public class Square : Shape
{
private int _side;
public Square(int side)
{
_side = side;
}
public override int GetArea()
{
return _side * _side;
}
}
class Program
{
static void Main()
{
Square square = new Square(5);
Console.WriteLine($"Area of the square = {square.GetArea()}");
}
}
In dit voorbeeld is de klasse Shape abstract en bevat een abstracte methode GetArea. De Square-klasse neemt over van Shape en biedt een implementatie voor de GetArea methode. De Square klasse kan worden geïnstantieerd en de GetArea methode retourneert het gebied van het vierkant.
In de volgende regels wordt beschreven hoe het abstract trefwoord de overname beïnvloedt:
- Abstracte klassen: Een abstracte klasse kan niet rechtstreeks worden geïnstantieerd. De abstracte klasse is een basisklasse voor afgeleide klassen en de afgeleide klassen moeten implementaties bieden voor alle abstracte leden van de abstracte klasse.
- Abstracte methoden: Abstracte methoden worden gedeclareerd zonder enige implementatie in de abstracte klasse. Afgeleide klassen moeten deze methoden overschrijven en de implementatie bieden.
- Abstracte eigenschappen: net als abstracte methoden worden abstracte eigenschappen zonder implementatie gedeclareerd en moeten worden overschreven in afgeleide klassen.
Het abstract trefwoord in C# is een krachtig hulpmiddel voor het definiëren van onvolledige klassen en leden die moeten worden geïmplementeerd in afgeleide klassen. Het dwingt een contract af dat afgeleide klassen moeten volgen, zodat bepaalde methoden en eigenschappen worden geïmplementeerd. Het juiste gebruik van het abstract trefwoord bevordert een duidelijke afbakening van verantwoordelijkheden tussen basis- en afgeleide klassen.
Het gebruik van het trefwoord virtual onderzoeken
Het virtual trefwoord in C# wordt gebruikt om methoden en eigenschappen te definiëren die kunnen worden overschreven in afgeleide klassen. Een virtuele methode of eigenschap heeft een implementatie in de basisklasse, maar kan worden uitgebreid of gewijzigd in afgeleide klassen. Afgeleide klassen kunnen virtuele leden overrulen om hun eigen implementaties te bieden.
Bijvoorbeeld:
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal makes a sound");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Dog barks");
}
}
class Program
{
static void Main()
{
Animal animal = new Dog();
animal.MakeSound();
}
}
In dit voorbeeld definieert de klasse Animal een virtuele methode MakeSound waarmee een algemeen bericht wordt afgedrukt. De Dog-klasse neemt over van Animal en overschrijft de methode MakeSound om een specifiek bericht af te drukken. Wanneer u een Dog-object maakt en de methode MakeSound aanroept, wordt de overschreven implementatie in de Dog-klasse uitgevoerd.
In de volgende regels wordt beschreven hoe het virtual trefwoord de overname beïnvloedt:
- Virtuele methoden: een virtuele methode heeft een implementatie in de basisklasse, maar kan worden overschreven in afgeleide klassen.
- Virtuele eigenschappen: net als bij virtuele methoden hebben virtuele eigenschappen een implementatie in de basisklasse en kunnen worden overschreven in afgeleide klassen.
Het gebruik van het trefwoord sealed onderzoeken
Het sealed trefwoord in C# wordt gebruikt om te voorkomen dat een klasse- of klasselid wordt overgenomen of overschreven. Wanneer een klasse is gemarkeerd als sealed, kan deze niet worden gebruikt als basisklasse voor andere klassen. Wanneer een methode is gemarkeerd als sealed, kan deze niet worden overschreven in afgeleide klassen.
Bijvoorbeeld:
public class BaseClass
{
public virtual void Method1()
{
Console.WriteLine("Method1 in BaseClass");
}
public virtual void Method2()
{
Console.WriteLine("Method2 in BaseClass");
}
}
public class DerivedClass : BaseClass
{
public sealed override void Method1()
{
Console.WriteLine("Method1 in DerivedClass");
}
public override void Method2()
{
Console.WriteLine("Method2 in DerivedClass");
}
}
public class FinalClass : DerivedClass
{
// This class can't override Method1 because it's sealed in DerivedClass
public override void Method2()
{
Console.WriteLine("Method2 in FinalClass");
}
}
In dit voorbeeld erft de DerivedClass van de BaseClass en overschrijft de Method1 methode, waarbij deze als sealed gemarkeerd wordt. De Method2 functie wordt ook overschreven in de DerivedClass, maar is niet verzegeld. De FinalClass neemt over van DerivedClass en probeert de methode Method2 te overschrijven. De methode Method1 kan echter niet worden overschreven omdat deze is verzegeld in de DerivedClass.
In de volgende regels wordt beschreven hoe het sealed trefwoord de overname beïnvloedt:
- Verzegelde klassen: Een verzegelde klasse kan niet worden gebruikt als basisklasse voor andere klassen. Hiermee wordt overerving van de verzegelde klasse voorkomen.
- Verzegelde methoden: een verzegelde methode kan niet worden overschreven in afgeleide klassen. Hiermee voorkomt u verdere wijziging van de methode in afgeleide klassen.
- Verzegelde eigenschappen: net als verzegelde methoden kunnen verzegelde eigenschappen niet worden overschreven door afgeleide klassen.
Verzegelde klassen en methoden zijn handig als u verdere uitbreiding of wijziging van een klasse of methode wilt voorkomen. Ze bieden een manier om overname te beperken en ervoor te zorgen dat bepaalde leden ongewijzigd blijven.
Impliciete overerving van Object onderzoeken
In C# nemen alle klassen impliciet over van de Object-klasse. De Object-klasse definieert verschillende methoden die beschikbaar zijn voor alle klassen, zoals ToString, Equalsen GetHashCode. Als een klasse niet expliciet wordt overgenomen van een andere klasse, wordt deze standaard overgenomen van Object.
-
ToString: De methodeToStringretourneert een tekenreeks die het huidige object vertegenwoordigt. Standaard wordt de volledig gekwalificeerde naam van een klasse geretourneerd. -
Equals: de methodeEqualsvergelijkt twee objecten voor gelijkheid. Standaard worden de verwijzingen van de objecten vergeleken. -
GetHashCode: de methodeGetHashCoderetourneert een hash-code voor het huidige object. Bij standaardinstelling wordt de hash-code van de verwijzing van het object geretourneerd.
Bekijk het volgende codevoorbeeld waarmee drie Person objecten worden gemaakt en de overgenomen ToString, Equalsen GetHashCode methoden worden gedemonstreerd:
Person person1 = new Person { Name = "Alice", Age = 30 };
Person person2 = new Person { Name = "Alice", Age = 30 };
Person person3 = person1;
Console.WriteLine(person1.ToString());
Console.WriteLine(person1.Equals(person2));
Console.WriteLine(person1.GetHashCode());
Console.WriteLine(person1.Equals(person3));
public class Person
{
public string? Name { get; set; }
public int Age { get; set; }
}
public class Employee : Person
{
public int EmployeeNumber { get; set; }
public decimal Salary { get; set; }
}
// Output: Person
// False
// 32854180
// True
In dit codevoorbeeld retourneert de ToString methode de volledig gekwalificeerde naam van de Person-klasse, die de gedefinieerde naamruimte bevat. De eerste Equals methode vergelijkt de verwijzingen van de person1- en person2-objecten en retourneert False. De methode GetHashCode retourneert de hashcode van de verwijzing van het person1-object. Vervolgens vergelijkt de methode Equals de verwijzingen van de person1 en person3 objecten en retourneert True.
Samenvatting
Overname stelt afgeleide klassen in staat om basisklasseleden over te nemen die een gemeenschappelijke set kenmerken en gedrag definiëren. Afgeleide klassen kunnen het gedrag van de basisklasse uitbreiden of wijzigen door nieuwe leden toe te voegen of bestaande leden te overschrijven. De abstract, virtual en sealed trefwoorden bepalen hoe basisklasseleden worden overgenomen of overschreven. De zichtbaarheid van overgenomen leden wordt beïnvloed door wijzigingsaanpassingen voor toegang, zoals public, protected, internalen private. Alle klassen in C# nemen impliciet over van de Object-klasse, die verschillende methoden biedt, zoals ToString, Equalsen GetHashCode, die beschikbaar zijn voor alle klassen.