Настроювання базових і похідних класів

Завершено

Терміни базового класу та похідні класу використовуються для опису зв'язків між класами в ієрархії успадкування. Базовий клас використовується для визначення загального набору атрибутів і поведінки, які успадковують інші класи. Похідний клас використовується для визначення спеціалізованого набору атрибутів і поведінки, які розширюють або змінюють базовий клас.

Перевірка транзитивного характеру успадкування

У C#клас може успадковувати лише один базовий клас. Це обмеження називається єдиним успадкуванням. Єдиний спадок дає змогу похідному класу отримати доступ до властивостей і методів єдиного базового класу, сприяючи чіткій і простій ієрархії. Концепція множинного успадкування не підтримується в C#. Це обмеження дозволяє уникнути складнощів і неоднозначностей, які можуть виникнути під час успадкування від кількох базових класів. Натомість C# використовує інтерфейси для досягнення подібних функцій.

Хоча клас може успадковувати лише один базовий клас, успадкування є транзитним. Це означає, що якщо клас C успадковує від класу B, а клас Б успадковує від класу А, то клас C успадковує членів, оголошених як у класі B, так і в класі А. Ця властивість забезпечує більш глибокі ієрархії та подальше повторне використання коду.

Наприклад, розгляньте систему керування транспортними засобами, у якій є базовий клас Vehicle, похідний Car класу, який успадковує від Vehicle, та інший похідний клас ElectricCar, який успадковує від Car. У цій ієрархії ElectricCar успадковує учасників як від Car, так і від Vehicle, демонструючи транзитивний характер успадкування.

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.");
    }
}

У цьому прикладі показано просту систему керування транспортними засобами з такими класами:

  • Vehicle клас. Базовий клас Vehicle містить спільні властивості та методи для всіх транспортних засобів, таких як Make, Model, StartEngine і StopEngine.

  • клас Car. Клас Car успадковує Vehicle і додає певні властивості та методи для автомобілів, як-от NumberOfDoors, OpenTrunk, HonkHornта LockDoors.

  • клас ElectricCar. Клас ElectricCar успадковує Car і додає властивості та методи, характерні для електрокарів, наприклад BatteryCapacity, ChargeBatteryта DisplayBatteryStatus.

  • клас CombustionEngineCar. Клас CombustionEngineCar також успадковує Car і додає властивості та методи, характерні для автомобілів із двигуном згоряння, наприклад FuelCapacity, Refuelта CheckOilLevel.

У цьому прикладі клас Car успадковує членів класу Vehicle, а ElectricCar та CombustionEngineCar успадковують учасників від класу Car. Ця ієрархія демонструє транзитивний характер успадкування, де ElectricCar та CombustionEngineCar успадковують членів як від Car, так і від Vehicle.

Перевірка успадкування та видимості учасників

Коли клас успадковує базовий клас, застосовуються такі правила:

  • Статичні конструктори та конструктори екземплярів не успадковуються.
  • Усі інші учасники базового класу успадковуються, але модифікатори доступу впливають на їхню видимість у похідних класах. Модифікатори доступу включають: public, protected, internalта private.

Загальнодоступні учасники

Загальнодоступні учасники доступні з будь-якого коду, який має доступ до класу. Похідні класи успадковують загальнодоступних членів, і вони доступні поза ієрархією класу.


public class BaseClass
{
    public int publicField;
    public void PublicMethod() { }
}

public class DerivedClass : BaseClass
{
    public void AccessPublicMember()
    {
        publicField = 10;
        PublicMethod();
    }
}

У цьому прикладі DerivedClass успадковує publicField та PublicMethod учасників від BaseClass. Метод AccessPublicMember в DerivedClass може отримати доступ до цих учасників. Загальнодоступні учасники також доступні з коду, який виходить за межі ієрархії класу.

Захищені учасники

Захищені учасники доступні в класі, у якому їх оголошено, і в межах похідних класів. Вони недоступні поза ієрархією класу.


public class BaseClass
{
    protected int protectedField;
    protected void ProtectedMethod() { }
}

public class DerivedClass : BaseClass

{
    public void AccessProtectedMember()
    {
        protectedField = 10;
        ProtectedMethod();
    }
}

У цьому прикладі DerivedClass успадковує protectedField та ProtectedMethod учасників від BaseClass. Метод AccessProtectedMember в DerivedClass може отримати доступ до цих учасників. Однак, якщо спробувати отримати доступ до захищених членів поза ієрархією класу, створюється помилка компіляції часу.

Внутрішні учасники

Внутрішні учасники доступні в одному зібранні. Вони недоступні поза складанням, навіть якщо клас успадковується.


public class BaseClass
{
    internal int internalField;
    internal void InternalMethod() { }
}

public class DerivedClass : BaseClass
{
    public void AccessInternalMember()
    {
        internalField = 10;
        InternalMethod();
    }
}

У цьому прикладі DerivedClass успадковує internalField та InternalMethod учасників від BaseClass. Метод AccessInternalMember в DerivedClass може отримати доступ до цих учасників, оскільки вони в одному зібранні. Однак, якщо спробувати отримати доступ до внутрішніх членів поза складанням, створюється помилка компіляції часу.

Приватні учасники

Приватні учасники доступні лише в класі, у якому їх оголошено. Похідні класи не успадковують приватних членів, тому вони не доступні безпосередньо в похідних класах.


public class BaseClass
{
    private int privateField;
    private void PrivateMethod() { }
}

public class DerivedClass : BaseClass
{
    public void AccessPrivateMember()
    {
        // Can't access privateField or PrivateMethod
    }
}

У цьому прикладі DerivedClass успадковує BaseClass, але не може отримати доступ до privateField або PrivateMethod учасників, оскільки вони приватні.

Перевірка використання абстрактних, віртуальних і запечатаних ключових слів у базовому класі

Базові та похідні класи використовують abstract, virtualта sealed ключові слова, щоб керувати поведінкою успадкованих учасників. Ці ключові слова дають змогу визначити рівень контролю, який похідні класи мають над учасниками базового класу.

Перевірте використання ключового слова abstract

Ключове слово abstract в C# використовується для визначення класів і членів класу, які є неповними та мають бути реалізовані в похідних класах. Абстрактний клас не можна створити безпосередньо та бути базовим класом для інших класів. Абстрактні методи та властивості оголошуються без будь-якого впровадження та мають бути перезаписовані в неабстрактних класах.

Наприклад:

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()}");
    }
}

У цьому прикладі клас Shape абстрактний і містить абстрактний метод GetArea. Клас Square успадковує Shape і забезпечує реалізацію методу GetArea. Можна створити екземпляр Square класу, а метод GetArea повертає область квадрата.

У наведених нижче правилах описано, як ключове слово abstract впливає на успадкування.

  • Абстрактні класи: абстрактний клас не можна створити безпосередньо. Абстрактний клас є базовим класом для похідних класів, і похідні класи повинні забезпечувати реалізацію для всіх абстрактних членів абстрактного класу.
  • Абстрактні методи: абстрактні методи оголошуються без будь-якого впровадження в абстрактному класі. Похідні класи мають перевизначити ці методи та забезпечити реалізацію.
  • Абстрактні властивості: подібно до абстрактних методів, абстрактні властивості оголошуються без реалізації і повинні бути перезаписовані в похідних класах.

Ключове слово abstract в C# – це потужний інструмент для визначення неповних класів і учасників, які мають бути реалізовані в похідних класах. Він забезпечує виконання контракту, який похідні класи повинні слідувати, гарантуючи, що певні методи і властивості реалізуються. Відповідне використання ключового слова abstract сприяє чіткому окресленню обов'язків між базовими та похідними класами.

Перевірте використання ключового слова virtual

Ключове слово virtual в C# використовується для визначення методів і властивостей, які можна перевизначення в похідних класах. Віртуальний метод або властивість має реалізацію в базовому класі, але його можна розширити або змінити в похідних класах. Похідні класи можуть перевизначити віртуальних членів для забезпечення власних реалізацій.

Наприклад:


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();
    }
}

У цьому прикладі клас Animal визначає віртуальний метод MakeSound, який друкує загальне повідомлення. Клас Dog успадковує Animal і замінює метод MakeSound для друку певного повідомлення. Коли ви створюєте об'єкт Dog і викликаєте метод MakeSound, виконується перевизначення в Dog класі.

У наведених нижче правилах описано, як ключове слово virtual впливає на успадкування.

  • Віртуальні методи: віртуальний метод має впровадження в базовому класі, але його можна перевизначено в похідних класах.
  • Віртуальні властивості: подібно до віртуальних методів, віртуальні властивості мають реалізацію в базовому класі і можуть бути перевизначені в похідних класах.

Перевірте використання ключового слова sealed

Ключове слово sealed в C# використовується, щоб запобігти успадкуванню або перевизначенню класу або члена класу. Якщо клас позначено як sealed, його не можна використовувати як базовий клас для інших класів. Якщо метод позначено як sealed, його не можна перевизначити в похідних класах.

Наприклад:


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");
    }
}

У цьому прикладі DerivedClass успадковує BaseClass і замінює метод Method1, позначаючи його як sealed. Метод Method2 також перезаписується в DerivedClass, але не запечатаний. FinalClass успадковує DerivedClass і намагається перевизначити метод Method2. Однак не можна перевизначити метод Method1, оскільки його запечатано в DerivedClass.

У наведених нижче правилах описано, як ключове слово sealed впливає на успадкування.

  • Запечатані класи: запечатаний клас не можна використовувати як базовий клас для інших класів. Він запобігає успадкування від запечатаних класів.
  • Запечатані методи: запечатаний метод не можна перевизначення в похідних класах. Він запобігає подальшій зміні методу в похідних класах.
  • Герметичні властивості: подібно до запечатаних методів, герметичні властивості не можна перезаписувати в похідних класах.

Запечатані класи та методи корисні, якщо потрібно запобігти подальшому розширенню або зміненню класу чи методу. Вони надають спосіб обмежити успадкування та переконатися, що певні члени залишаються незмінними.

Перевірка неявного успадкування з об'єкта

У C#всі класи неявно успадковуються від класу Object. Клас Object визначає кілька способів, доступних для всіх класів, наприклад ToString, Equalsта GetHashCode. Якщо клас явно не успадковується від іншого класу, він усе одно успадковується від Object за замовчуванням.

  • ToString: метод ToString повертає рядок, який представляє поточний об'єкт. За замовчуванням вона повертає повне ім'я класу.
  • Equals: метод Equals порівнює два об'єкти за рівність. За замовчуванням він порівнює посилання на об'єкти.
  • GetHashCode: метод GetHashCode повертає код гешування для поточного об'єкта. За замовчуванням повертає геш-код посилання об'єкта.

Розглянемо наведений нижче зразок коду, який створює три Person об'єкти та демонструє успадковані ToString, Equalsта методи GetHashCode.

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

У цьому прикладі коду метод ToString повертає повне ім'я класу Person, яке включає визначений простір імен. Перший метод Equals порівнює посилання на person1 та person2 об'єкти та повертає False. Метод GetHashCode повертає геш-код посилання person1 об'єкта. А потім метод Equals порівнює посилання на person1 та person3 об'єкти та повертає True.

Зведення

Успадкування дає змогу похідним класам успадковувати членів базового класу, які визначають загальний набір атрибутів і поведінки. Похідні класи можуть розширити або змінити поведінку базового класу, додавши нових учасників або перевизначивши наявних учасників. Ключові слова abstract, virtualта sealed використовуються для керування успадкуванням або перевизначення базових членів класу. На видимість успадкованих учасників впливають модифікатори доступу, наприклад public, protected, internalта private. Усі класи в C# неявно успадковуються від класу Object, який надає кілька методів, наприклад ToString, Equalsта GetHashCode, доступних для всіх класів.