Изучение полиморфизма на основе наследования

Завершено

Полиморфизм на основе наследования основан на иерархии классов, где производные классы наследуют поведение и свойства базового класса. Отношение наследования позволяет рассматривать объекты производных классов как объекты базового класса. Возможность рассматривать производные объекты класса как объекты базового класса позволяет писать код, который работает с несколькими типами объектов, не зная конкретный тип во время компиляции.

В следующем примере кода демонстрируется полиморфизм на основе наследования в C#:


// Base class
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("The animal makes a sound.");
    }
}

// Derived class Dog
public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("The dog barks.");
    }
}

// Derived class Cat
public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("The cat meows.");
    }
}

// Derived class Cow
public class Cow : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("The cow moos.");
    }
}

class Program
{
    static void Main()
    {
        // Create an array of Animal objects
        Animal[] animals = new Animal[3];

        Animal animal1 = new Dog();
        Animal animal2 = new Cat();
        Animal animal3 = new Cow();

        animals[0] = animal1;
        animals[1] = animal2;
        animals[2] = animal3;

        // Demonstrate polymorphism
        foreach (Animal animal in animals)
        {
            animal.MakeSound();
        }
    }
}

В этом примере кода обратите внимание, что класс Program создает массив объектов Animal и назначает экземпляры Dog, Catи Cow элементам массива. Метод MakeSound вызывается для каждого объекта в массиве, демонстрируя полиморфизм. Метод MakeSound переопределяется в производных классах, чтобы обеспечить определенное поведение для каждого типа животных.

Приведение объекта базового класса к производного класса

Приведение в C# — это процесс преобразования объекта одного типа в другой тип. Приведение часто используется при реализации полиморфизма с помощью иерархий наследования, где имеется базовый класс и один или несколько производных классов.

Существует два основных типа приведения:

  • Неявное приведение: это происходит автоматически при преобразовании производного класса в базовый класс. Это безопасно, так как каждый экземпляр производного класса также является экземпляром базового класса.

    
    BankAccount account = new CheckingAccount();
    
    
  • Явное приведение. Для этого требуется оператор приведения и используется при преобразовании базового класса в производный класс. Это не всегда безопасно, так как не каждый экземпляр базового класса является экземпляром производного класса.

    
    CheckingAccount checkingAccount = (CheckingAccount)account;
    
    

Приведение объектов с помощью ключевых слов is и as

В C# можно приводить объекты с помощью ключевых слов is и as. Эти ключевые слова обеспечивают безопасный способ проверки типа объекта перед приведением его к другому типу. Ниже приведены некоторые распространенные способы приведения объектов в C#:

  • Использование ключевого слова is с сопоставлением шаблонов:

    
    if (account is CheckingAccount checkingAccount)
    {
        // Use checkingAccount as a CheckingAccount
    }
    
    
    • Этот синтаксис проверяет, имеет ли account тип CheckingAccount.
    • Если проверка выполнена успешно, она приводит account к CheckingAccount и назначает ее переменной checkingAccount.
    • Этот подход является кратким и безопасным, так как он объединяет проверку типа и приведение на одном шаге.
  • Использование ключевого слова is, за которым следует явное приведение:

    
    if (account is CheckingAccount)
    {
        CheckingAccount checkingAccount = (CheckingAccount)account;
        // Use checkingAccount as a CheckingAccount
    }
    
    
    • Этот синтаксис проверяет, имеет ли account тип CheckingAccount.
    • Если проверка выполнена успешно, она явно приводит account к CheckingAccount и назначает ее переменной checkingAccount.
    • Этот подход более подробный, чем синтаксис сопоставления шаблонов, но обеспечивает больший контроль над процессом приведения.
  • Использование ключевого слова as:

    
    CheckingAccount checkingAccount = account as CheckingAccount;
    
    if (checkingAccount != null)
    {
        // Use checkingAccount as a CheckingAccount
    }
    
    
    • Этот синтаксис пытается привести account к CheckingAccount и назначает результат checkingAccount.
    • Если приведение выполнено успешно, checkingAccount содержит объект приведения; в противном случае значение null.
    • Этот подход полезен, если необходимо проверить результат приведения перед использованием объекта приведения. Например, если вы хотите избежать исключений и корректно обрабатывать случай сбоя.
  • При реализации приведения рассмотрите следующие рекомендации.

    • Использование сопоставления шаблонов с is: объединяет проверку типа и приведение на одном шаге.
    • Использование ключевого слова is с явным приведением: разделяет проверку типа и приведение на два шага, обеспечивая больше контроля над процессом приведения.
    • Используя ключевое слово as: пытается привести и обрабатывает сбой, возвращая значение NULL.

Понимание этих методов приведения является важным для работы с полиморфизмом и наследованием в C#.

Избегайте распространенных ошибок при реализации полиморфизма

Когда ваша цель состоит в полиморфизме на основе наследования, вот некоторые вещи, чтобы избежать, и некоторые вещи, чтобы обеспечить следующее:

  • Избегайте использования запечатанных классов и методов: запечатанные классы и методы не могут быть унаследованы или переопределены, что ограничивает возможность использования полиморфизма. Если вы запечатываете класс или метод, вы не можете продолжить расширение и настройку. Например:

    
    public sealed class BankAccount { } // This class can't be inherited
    
    
  • Избегайте чрезмерного использования статических методов. Статические методы относятся к самому классу, а не экземпляру класса. Они не могут быть переопределены, что означает, что они не участвуют в полиморфизме.

    
    public static void PrintMessage() { } // This method can't be overridden
    
    
  • Избегайте жесткой связи. Жесткое связывание происходит, когда классы или компоненты в системе сильно зависят друг от друга. Это означает, что изменения в одном классе могут напрямую влиять на другие классы, что делает систему менее гибкой и сложной для поддержания. Жесткое связывание может привести к трудностям при тестировании, расширении и изменении кода.

    
    public class BankAccount
    {
        public void TransferFunds(SavingsAccount savingsAccount)
        {
            // Tight coupling with SavingsAccount
        }
    }
    
    
  • Избегайте использования ключевого слова new без хорошей причины. Ключевое слово "new" скрывает метод базового класса в производном классе, что может привести к путанице и неожиданному поведению. Используйте только ключевое слово new, если планируется скрыть метод базового класса намеренно.

    
    public class Dog : Animal
    {
        public new void MakeSound() // Hides the base class method. Better to use 'override'
        {
            Console.WriteLine("The dog barks.");
        }
    }
    
    
  • Убедитесь, что согласованные подписи методов. Убедитесь, что переопределенные методы в производных классах имеют ту же сигнатуру, что и метод базового класса. Изменение сигнатуры метода приведет к скрытию метода, а не переопределении.

    
    public class Animal
    {
        public virtual void MakeSound(string sound)
        {
            Console.WriteLine("The animal makes a sound.");
        }
    }
    
    public class Dog : Animal
    {
        // Method signature doesn't match the base class
        public override void MakeSound()
        {
            Console.WriteLine("The dog barks.");
        }
    }
    
    

Сводка

Полиморфизм на основе наследования в C# позволяет создать иерархию классов, в которых производные классы наследуют поведение и свойства базового класса. Этот механизм наследования позволяет рассматривать объекты производных классов как объекты базового класса. Возможность рассматривать производные объекты класса как члены базового класса позволяет писать код, который работает с несколькими типами объектов, не зная конкретный тип во время компиляции. Понимая методы приведения, избегая распространенных ошибок и следуя рекомендациям, вы можете эффективно реализовать полиморфизм в приложениях C#.