Наследование — это один из базовых принципов объектно-ориентированного программирования (ООП). Если попытаться найти более менее полное определение того, что из себя представляет наследование в ООП, то можно встретить, например, такое определение: «Наследование (англ. inheritance) — концепция объектно-ориентированного программирования, согласно которой абстрактный тип данных может наследовать данные и функциональность некоторого существующего типа, способствуя повторному использованию компонентов программного обеспечения.» На первый взгляд, может быть это определение непонятно, но мы с Вами не пишем академическую работу по наследованию в ООП, а учимся использовать механизмы наследования в C#, поэтому рассмотрим этот вопрос на практике.
Простой пример наследования в C#
Чтобы понять суть наследования в C# вернемся к нашему классу Building
, который описывает некое здание. Этот класс описывает размеры здания (длину, ширину и высоту) и даже имеет метод расчёта объема и несколько свойств. Мы с этим классом можем работать как угодно в рамках его функциональности. А теперь представим, что нам потребовалось добавить в программу работу не только с некими абстрактными зданиями, а конкретизировать их назначение и свойства, например, нам необходим класс, который будет описывать жилой дом. В этом случае мы можем пойти двумя путями:
- Создать новый класс, описать в нем те же свойства, что и у класса
Building
и добавить интересующие нас свойства. Несмотря на то, что такой подход вполне сработает, делать так категорически не стоит. О том, почему так не стоит делать мы узнаем ниже, когда ощутим все преимущества наследования в C#. - Наследовать новый класс от класса
Building
и дописать необходимые свойства и методы.
Итак, назовем наш новый класс House
и наследуем его от класса Building
. О том, как в Visual Studio C# создать заготовку класса рассказано здесь. Вот как может выглядеть наш новый класс:
internal class House: Building { private int windowCount; public int WindowCount { get => windowCount; set => windowCount = value >= 0 ? value : throw new Exception("Количество окон не может быть меньше нуля"); } }
В абстрактном виде этот код можно прочитать следующим образом:
класс House унаследован от класса Building начало описания Свойство "Количество окон" конец описания
При этом, обратите внимание на то, что мы не повторялись в описании нового класса и не переносили в него свойства и методы класса Building
— они наследовались в новом классе и мы их можем спокойно использовать, например (про упрощенную инициализацию объектов в C# смотри здесь),
using FirstClass; House house = new House { Name ="Жилой дом №1", Height = 5, Length = 15, Width = 20, WindowCount = 5 }; Console.WriteLine($"Объем дома {house.Volume}"); Console.WriteLine($"Количество окон {house.WindowCount}"); Console.WriteLine($"{house}");
Как видите, мы использовали все возможности класса Building
, а также воспользовались новым свойством WindowCount
, используя механизмы наследования в C#. В этом-то и кроется вся мощь наследования — мы не повторяемся в коде и наделяем наследников только теми свойствами и методами, которые характерны именно для них, а все общие свойства наследуются автоматически от предков. Вот почему выше я написал, что первый путь создания нового класса в корне не верен и контрпродуктивен во всех смыслах.
Особенности наследования в C#
Как и любой язык программирования, C# обладает своими возможностями и особенностями, которые нам следует помнить и знать. Что касается наследования, то в C# следует помнить, что наследование классов может осуществляться последовательно. Например,
internal class MultiStoreyBuilding: House { public int FloorCount { get; set; } }
здесь для упрощения кода использовано автосвойство FloorCount
. Теперь цепочка наследования выглядит следующим образом: Building -> House -> MultiStoreyBuilding
и класс MultiStoreyBuilding обладает всеми свойствами как для класса Building
, так и для House
:
MultiStoreyBuilding house = new MultiStoreyBuilding { Name ="Жилой дом №1", Height = 5, Length = 15, Width = 20, WindowCount = 5, FloorCount = 5, };
При этом в C# мы не можем использовать множественное наследование от нескольких классов. Например, следующий код не скомпилируется:
internal class MultiStoreyBuilding: House, Building { public int FloorCount { get; set; } }
и Visual Studio выдаст следующую ошибку:
Для того, чтобы создать класс-наследник нескольких базовых классов нам необходимо идти последовательно, расширяя функциональность нашей программы. При этом, никто не запрещает нам в C# создавать несколько различных наследников от одного базового класса. Например, мы бы могли унаследовать от класса Building
сразу несколько классов — частный дом, многоквартирный дом, сарай и так далее.
Такой подход вполне оправдан, интуитивно понятен и довольно широко используется также и в других объектно-ориентированных языках программирования
Итого
Итого, сегодня мы познакомились с тем, что из себя представляет наследование в C# и создали новый класс-наследник. В C# используется последовательный подход к наследованию в рамках которого класс может наследоваться только от одного базового класса.