Доступ к элементам базового класса из производного класса

Завершено

Производный класс, заменяющий или переопределяет метод базового класса или свойство, по-прежнему может получить доступ к методу или свойству базового класса с помощью ключевого слова base. Это позволяет вызывать конструкторы базового класса, методы и свойства из переопределенных членов производного класса. Используя ключевое слово base, можно убедиться, что производный класс правильно использует и расширяет функциональные возможности, предоставляемые базовым классом.

Ключевое слово base используется для выполнения следующих задач:

  • Вызов метода базового класса из переопределенного метода производного класса.
  • Реализация конструктора базового класса из конструктора производного класса.

Ключевое слово base имеет следующие ограничения:

  • Ключевое слово base можно использовать только в конструкторе, методе экземпляра или методе доступа к свойству экземпляра.
  • Ключевое слово base нельзя использовать в статическом методе. Попытка использовать ключевое слово base в статическом методе приведет к возникновению ошибки.

При реализации ключевого слова base в производном классе код использует базовый класс, указанный в объявлении класса. Например, если в объявлении класса указано ClassC : ClassB класса, ключевое слово base позволяет коду получать доступ к элементам ClassB из ClassC. Не имеет значения, наследуется ли класс ClassB от класса ClassA.

Доступ к свойствам и методам базового класса из производного класса

Доступ к свойствам и методам базового класса из производного класса является общим требованием при реализации наследования. Следующий код демонстрирует синтаксис реализации ключевого слова base:

base.MemberName

Обратите внимание, что ключевое слово base и имя элемента базового класса разделяются точкой (.). MemberName может быть свойством, методом или полем базового класса.

В следующем коде показано, как получить доступ к свойствам и методам базового класса из производного класса:


// create an instance of the derived classes
DerivedClass1 derivedClass1 = new DerivedClass1();

// demonstrate the overridden methods of the derived class
Console.WriteLine("Calling the methods of DerivedClass1...\n");
Console.WriteLine($"Method1 in the derived class overrides the base class and provides a custom message: {derivedClass1.Method1()}");

derivedClass1.Method2();

/*Output:
Calling the methods of DerivedClass1...

Method1 in the base class is abstract and can't be called.
Method1 in the derived class overrides the base class and provides a custom message: Message from the overridden Method1 in DerivedClass1

Method2 in the derived class calls base.Method2 (reuses the base class implementation)
Method2 in the base class implements common behavior and returns true/false
Method2 in the derived class accesses base class Property2: Base - Property2

Method2 in the derived class uses base class members to modify and extend functionality
*/

public abstract class BaseClass
{
    public abstract string Property1 { get; set; }
    public virtual string Property2 { get; set; } = "Base - Property2";

    public abstract string Method1();

    public virtual bool Method2()
    {
        Console.WriteLine("Method2 in the base class implements common behavior and returns true/false");
        return true;
    }
}

public class DerivedClass1 : BaseClass
{
    public override string Property1 { get; set; } = "Derived - Property1";
    public new string Property2 { get; set; } = "Derived - Property2";

    public override string Method1()
    {
        // Method1 of the base class is abstract and can't be called
        Console.WriteLine($"Method1 in the base class is abstract and can't be called.");
        
        // Return the base.Method1 response additional information specific to DerivedClass1
        return $"Message from the overridden Method1 in DerivedClass1";
    }

    public override bool Method2()
    {
        // Call base class method to implement common behavior
        Console.WriteLine($"\nMethod2 in the derived class calls base.Method2 (reuses the base class implementation)");
        bool baseMethod2Success = base.Method2();

        // Access base class Property2
        string baseProperty2Value = base.Property2;
        Console.WriteLine($"Method2 in the derived class accesses base class Property2: {baseProperty2Value}");

        // implement derived class logic that involves base class information
        if (baseMethod2Success && baseProperty2Value == "Base - Property2")
        {
            Console.WriteLine("\nMethod2 in the derived class uses base class members to modify and extend functionality");
            return true;
        }
        else
        {
            Console.WriteLine("Method2 in the derived class can implement alternate behavior");
        }

        return false;
    }
}

В этом примере кода класс DerivedClass1 наследует от BaseClass и переопределяет его методы для расширения их функциональных возможностей. Код реализует следующие действия:

  1. Экземпляр DerivedClass1 создается с помощью инструкции DerivedClass1 derivedClass1 = new DerivedClass1();. Этот экземпляр производного класса используется для вызова и демонстрации переопределенных методов производного класса.

  2. Код вызывает Method1 производного класса из оператора Console.WriteLine. Переопределенный Method1 не может получить доступ к абстракту Method1 базового класса. Вместо этого она предоставляет собственную реализацию, которая возвращает строку, указывающую, что она уникальна для производного класса.

  3. Код вызывает Method2 производного класса. Переопределенный Method2 использует base.Method2 для повторного использования Method2 реализации базового класса. Метод обращается к Property2 базового класса и выводит его значение. Затем переопределенный метод использует логическое значение, возвращаемое Method2 базового класса, и значение Property2, чтобы расширить функциональность Method2 или реализовать альтернативное поведение. На этом шаге показано, как производный класс может создавать и изменять поведение метода базового класса.

Заметка

Рекомендуется использовать base виртуальных членов для вызова реализации базового класса этого члена в собственной реализации. Разрешение поведения базового класса позволяет производным классам сосредоточиться на реализации поведения, конкретного для производного класса. Если реализация базового класса не вызывается, то он до производного класса, чтобы обеспечить совместимость их поведения с поведением базового класса.

Доступ к конструкторам базового класса из производного класса

Конструкторы классов в базовом классе можно получить из конструкторов производного класса с помощью ключевого слова base.

В следующем коде показано, как получить доступ к конструкторам базового класса из производного класса:


// create an instance of the derived classes
DerivedClass1 derivedClass1 = new DerivedClass1("Derived1 - Property1", "Derived1 - Property2");

// demonstrate the overridden methods of the derived class
Console.WriteLine("Calling the methods of DerivedClass1...\n");
Console.WriteLine($"Method1 in the derived class appends derived class information to the return value: {derivedClass1.Method1()}");

derivedClass1.Method2();

/*Output:
Calling the methods of DerivedClass1...

Method1 in the derived class calls base.Method1. base.Method1 returns: base.Method1 results
Method1 in the derived class appends derived class information to the return value: base.Method1 results - plus information specific to DerivedClass1

Method2 in the derived class calls base.Method2 (reuses the base class implementation)
Method2 in the base class implements common behavior and returns true/false
Method2 in the derived class accesses base class Property2: Base - Property2

Method2 in the derived class uses base class members to modify and extend functionality
*/

public abstract class BaseClass
{
    public abstract string Property1 { get; set; }
    public virtual string Property2 { get; set; }

    public BaseClass(string property1, string property2)
    {
        Property1 = property1;
        Property2 = property2;
    }

    public virtual string Method1()
    {
        return "base.Method1 results";
    }

    public virtual bool Method2()
    {
        Console.WriteLine("Method2 in the base class implements common behavior and returns true/false");
        return true;
    }
}

public class DerivedClass1 : BaseClass
{
    public override string Property1 { get; set; }
    public new string Property2 { get; set; }

    public DerivedClass1(string property1, string property2) : base(property1, "Base - Property2")
    {
        Property1 = property1;
        Property2 = property2;
    }

    public override string Method1()
    {
        // Method1 of the base class is now virtual
        Console.WriteLine($"Method1 in the derived class calls base.Method1. base.Method1 returns: {base.Method1()}");
        
        // Return the base.Method1 response additional information specific to DerivedClass1
        return $"{base.Method1()} - plus information specific to DerivedClass1";
    }

    public override bool Method2()
    {
        // Call base class method to implement common behavior
        Console.WriteLine($"\nMethod2 in the derived class calls base.Method2 (reuses the base class implementation)");
        bool baseMethod1Success = base.Method2();

        // Access base class Property2
        string baseProperty2Value = base.Property2;
        Console.WriteLine($"Method2 in the derived class accesses base class Property2: {baseProperty2Value}");

        // implement derived class logic that involves base class information
        if (baseMethod1Success && baseProperty2Value == "Base - Property2")
        {
            Console.WriteLine("\nMethod2 in the derived class uses base class members to modify and extend functionality");
            return true;
        }
        else
        {
            Console.WriteLine("Method2 in the derived class can implement alternate behavior");
        }

        return false;
    }
}

Вызов конструктора базового класса из производного конструктора классов важен. Например, когда конструктор базового класса инициализирует общие свойства, используемые производным классом. Вызывая конструктор базового класса из конструктора производных классов, необходимо убедиться, что общие свойства инициализированы до выполнения конструктора производных классов.

Рассмотрим следующий код:


public abstract class Vehicle
{
    public virtual int Speed { get; set; }
    public virtual int Fuel { get; set; }

    public Vehicle(int speed, int fuel)
    {
        Speed = speed;
        Fuel = fuel;
    }

    public virtual void Drive()
    {
        Console.WriteLine("Vehicle is driving");
    }
}

public class Car : Vehicle
{
    public int NumberOfDoors { get; set; }
    public int TrunkCapacity { get; set; }

    public Car(int speed, int fuel, int numberOfDoors) : base(speed, fuel)
    {
        NumberOfDoors = numberOfDoors;
        // Initialize TrunkCapacity based on the Speed and Fuel properties of the base class
        TrunkCapacity = CalculateTrunkCapacity(Speed, Fuel);
    }

    private int CalculateTrunkCapacity(int speed, int fuel)
    {
        // Example logic to calculate trunk capacity based on speed and fuel
        return (speed + fuel) / 2;
    }

    public override void Drive()
    {
        base.Drive();
        Console.WriteLine("Car is driving with additional features");
    }
}

class Program
{
    static void Main()
    {
        Car car = new Car(100, 50, 4);
        Console.WriteLine($"Speed: {car.Speed}, Fuel: {car.Fuel}, NumberOfDoors: {car.NumberOfDoors}, TrunkCapacity: {car.TrunkCapacity}");
        car.Drive();
    }
}

Обратите внимание, что конструктор классов Car вызывает конструктор базового класса с помощью ключевого слова base. Вызов конструктора базового класса сначала гарантирует правильность инициализации свойств Speed и Fuel базового класса Vehicle перед выполнением конструктора классов Car. Эта последовательность событий важна, так как свойство TrunkCapacity класса Car инициализируется с помощью свойств Speed и Fuel базового класса Vehicle.

Сводка

При доступе к членам базового класса из производного класса следует учитывать следующие рекомендации.

  • Используйте ключевое слово base для доступа к членам базового класса из производного класса.
  • Используйте ключевое слово base для вызова конструкторов базового класса из производных конструкторов классов.
  • Используйте ключевое слово base для доступа к полям базового класса, свойствам и методам из переопределенных методов в производном классе.