Наследование и полиморфизм в C#

View more categories:

1- Введение

Наследство и полиморфизм - это очень важное понятие в CSharp. И вы обязаны его понять при изучении.

2- Класс, объект и Конструктор

Вам нужно ясно понимать про класс, конструктор (constructor) и объект перед тем, как начать изучение отношений наследства в CSharp. Посмотрим класс  Person, описывающий человека с реляционной информацией.
Person.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    class Person
    {
        // Поле name - информация имена человека
        public String Name;

        // Поле bornYear - информация даты рождения
        public int BornYear;

        // Место рождения
        public String PlaceOfBirth;

        // Данный Constructor  имеет 3 параметра.
        // Цель инициализировать значения для полей Person.
        // Определить ясно имя, дату рождения, место рождения.
        public Person(String Name, int BornYear, String PlaceOfBirth)
        {
            this.Name = Name;
            this.BornYear = BornYear;
            this.PlaceOfBirth = PlaceOfBirth;
        }

        // Данный Constructor  имеет 2 параметра.
        // Цель инициализировать значения для 2 полей имени и даты рождения для Person.
        // Место рождения не инициализируется.
        public Person(String Name, int BornYear)
        {
            this.Name = Name;
            this.BornYear = BornYear;
        }

    }
}
PersonDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    class PersonDemo
    {

        static void Main(string[] args)
        {

            // Объект: Thomas Edison.
            // Создан с помощью Constructor с 2-мя параметрами класса Person.
            Person edison = new Person("Thomas Edison", 1847);

            Console.WriteLine("Info:");
            Console.WriteLine("Name: " + edison.Name);
            Console.WriteLine("Born Year: " + edison.BornYear);
            Console.WriteLine("Place Of Birth: " + edison.PlaceOfBirth);

            // Объект: Bill Gates
            // Создан с помощью Constructor с 3-мя параметрами класса Person.
            Person billGates = new Person("Bill Gate", 1955, "Seattle, Washington");

            Console.WriteLine("-----------------------------------");

            Console.WriteLine("Info:");
            Console.WriteLine("Name: " + billGates.Name);
            Console.WriteLine("Born Year: " + billGates.BornYear);
            Console.WriteLine("Place Of Birth: " + billGates.PlaceOfBirth);

            Console.ReadLine();
        }
    }
}
Результат запуска класса  PersonDemo:

Отличать Класс, Конструктор и Объект:

Класс  Person симулирует человека, это что-то абстрактное, но имеет поля для информации, в римере выше это имя, год рождения, место рождения.
Constructor.
  • Конструктор (Constructor) всегда имеет одинаковое название с классом.
  • Класс может содержать один или много Конструкторов (Constructor).
  • Constructor может иметь или не имееть параметра, Constructor не имеющий параметр еще называется - Constructor по умолчанию.
  • Constructor  используется для создания объекта определенного класса.
Таким образом класс  Person (Описывающий класс человека) является абстрактным, но при ясном указании на вас или на меня, то это 2 объекта принадлежащих классу  Person. И Constructor является особым методом для создания объектов, Constructor прикрепит значение к полям (field) созданных объектов.
Это иллюстрации как прикрепить значения полям (field)  класса, когда вы создаете объект из Constructor.

3- Наследственность в CSharp

Нам понадобятся некоторые классы для участия в примерах.
  • Animal: Класс симулирующий животное.
  • Duck: Класс симулирующий утку, подкласс Animal.
  • Cat: Класс симулирующий кошку, подкласс Animal
  • Mouse: Класс симулирующий мышь, подкласс​​​​​​​ Animal.
Как вы уже знаете, Constructor класса используется для создания объекта, и прикрепляет значение полям (field).
Constructor подкласса всегда вызывает к Constructor родительского класс, чтобы инициализировать значения для полей родительского класса, потом только инициализирует значение для его полей.
Посмотрим пример:
Animal.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    public abstract class Animal
    {
        // Это Поле Name (Имя).
        // например Кот Tom, Мышь Jerry.
        public string Name;

        // Constructor по умолчанию.
        public Animal()
        {
            Console.WriteLine("- Animal()");
        }

        public Animal(string Name)
        {
            // Прикрепить значение полю Name.
            this.Name = Name;
            Console.WriteLine("- Animal(string)");
        }

        // Move(): Животное двигается.
        // virtual: Разрешить подклассу переопределить (override) данный метод.
        public virtual void Move()
        {
            Console.WriteLine("Animal Move");
        }

        public void Sleep()
        {
            Console.WriteLine("Sleep");
        }

    }
}
Cat является подклассом унаследованным от класса  Animal, он также имеет свои поля.
Cat.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    public class Cat : Animal
    {

        public int Age;
        public int Height;

        // Это Constructor имеющий 3 параметра класса Cat.
        // Использовать :base(name) для вызова Constructor родительского класса: Animal(string).
        // Полям родительского класса прикрепится значение.
        // Затем, полям данного класса прикрепится значение.
        public Cat(string name, int Age, int Height)
            : base(name)
        {
            this.Age = Age;
            this.Height = Height;
            Console.WriteLine("- Cat(string,int,int)");
        }

        // Данный Constructor вызывает Constructor по умолчанию (Без параметров) родительского класса.
        public Cat(int Age, int Height)
            : base()
        {
            this.Age = Age;
            this.Height = Height;
            Console.WriteLine("- Cat(int,int)");
        }

        public void Say()
        {
            Console.WriteLine("Meo");
        }

        // Переопределить метод Move() родительского класса (Animal).
        // Переписать данный метод,
        // чтобы описать точно поведения движения котов.
        public override void Move()
        {
            Console.WriteLine("Cat Move ...");
        }
    }
}
CatTest.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    class CatTest
    {
        static void Main(string[] args)
        {

            Console.WriteLine("Create Cat object from Cat(string,int,int)");

            // Создать объект Cat с помощью конструктора с 3-мя параметрами.
            // Полю Name у Animal будет прикреплено значение "Tom".
            // Полю Age у Cat будет прикреплено значение 3
            // Полю Height у Cat будет прикреплено значение 20.
            Cat tom = new Cat("Tom",3, 20);

            Console.WriteLine("------");
            
            Console.WriteLine("Name = {0}", tom.Name);
            Console.WriteLine("Age = {0}", tom.Age);
            Console.WriteLine("Height = {0}", tom.Height);

            Console.WriteLine("------");

            // Вызвать метод унаследованный от класса Animal.
            tom.Sleep();

            // Вызвать метод Say() (класса Cat)
            tom.Say();


            Console.ReadLine();
        }
    }
}
Результат запуска класса  CatTest:
Что происходит когда вы создаете обхект из Constructor? Как он вызывает Constructor родительского класса? Смотрите иллюстрированное изображение ниже:
С иллюстрированным изображением выше вы видите, что Constructor родительского класса всегда вызывает сначала Constructor подкласса, он прикрепит значение сначала полям родительского класса, потом только прикрепляется значение полям подкласса.
Когда вы пишете Constructor и не определяете его на основании (base) какого Constructor родительского класса, CSharp по умолчанию понимает, что тот Constructor основывается на Constructor по умолчанию родительского класса.

// Данный Constructor не определяет 
// на основании (base) какого Constructor родительского класса.
public Cat(int Age, int Height)
{

}

// Он будет равен:
public Cat(int Age, int Height) : base()
{

}
Один Constructor может вызвать другой Constructor в одном классе используя  :this.
private int Weight;

// Constructor по умолчанию (Без параметра).
// Вызывает constructor Mouse(int).
public Mouse()    : this(100)
{
}

// Это constructor с 1 параметром.
// Не определяет :base
// То есть основано (base) на Constructor по умолчанию родительского класса.
public Mouse(int Weight)
{            
   this.Weight = Weight;
}
Mouse.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    public class Mouse : Animal
    {

        private int Weight;

        // Constructor по умолчанию (Не имеет параметра).
        // Вызывает Mouse(int)
        public Mouse()
            : this(100)
        {
        }

        // Данный Constructor имеет 1 параметр.
        // И не определяет :base
        // Значит он base (основывается) на Constructor по умолчанию родительского класса.
        public Mouse(int Weight)
        {            
            this.Weight = Weight;
        }

        // Данный Constructor имеет 2 параметра.
        public Mouse(String name, int Weight)
            : base(name)
        {
            this.Weight = Weight;
        }

        public int GetWeight()
        {
            return Weight;
        }

        public void SetWeight(int Weight)
        {
            this.Weight = Weight;
        }

 
    }
}
Используя оператор  'is' вы можете проверить является ли объект видом определенного класса или нет. Смотрите пример ниже:
IsOperatorDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    class IsOperatorDemo
    {
        static void Main(string[] args)
        {
            // Создать объект Animal.
            // Animal является абстрактным классом,
            // Поэтому вы не можете создать объект из Constructor у Animal.
            Animal tom = new Cat("Tom", 3, 20);

            Console.WriteLine("Animal Sleep:");

            // Вызвать метод Sleep() у Animal
            tom.Sleep();

            // Использовать оператор 'is' чтобы проверить
            // является ли объект определенным видом или нет.
            bool isMouse = tom is Mouse;// false

            Console.WriteLine("Tom is mouse? " + isMouse);

            bool isCat = tom is Cat; // true
            Console.WriteLine("Tom is cat? " + isCat);

            bool isAnimal = tom is Animal; // true
            Console.WriteLine("Tom is animal? " + isAnimal);

            Console.ReadLine();
        }
    }
}
Запуск примера:

Сast в CSharp.

CastDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    class CastDemo
    {
        static void Main(string[] args)
        {
            // Вызвать метол, он возвращает случайное животное.
            Animal animal = GetRandomAnimal();

            if (animal is Cat)
            {
                Console.WriteLine("Your animal is Cat");

                // Сast в объект Cat.
                Cat cat = (Cat)animal;

                // И получить достуа в поле Height объекта Cat.
                Console.WriteLine("Cat height: " + cat.Height);
            }
            else if (animal is Mouse)
            {
                Console.WriteLine("Your animal is Mouse");

                // Сastв объект Mouse.
                Mouse mouse = (Mouse)animal;

                // И вызвать метод класса Mouse.
                Console.WriteLine("Mouse weight: " + mouse.GetWeight());
            }

            Console.ReadLine();
        }

        // Метод возвращает случайное животное.
        public static Animal GetRandomAnimal()
        {
            // Возвращает случайное число между 0 и 9 (0,...9)
            int random = new Random().Next(0, 10);

            Console.WriteLine("random = " + random);

            Animal animal = null;
            if (random < 5)
            {
                Console.WriteLine("Create a Cat");
                animal = new Cat("Tom", 3, 20);
            }
            else
            {
                Console.WriteLine("Create a Mouse");
                animal = new Mouse("Jerry", 5);
            }
            return animal;
        }
    }
}
Запуск примера:

4- Полиморфизм в CSharp

Полиморфизм  (Polymorphism) значит иметь много форм. В модели объектно-ориентированного программирования, полиморфизм обычно описывается как "один интерфейс, много функций".

Полиморфизм может быть статическим или динамическим. В статическом полиморфизме, реакция на функцию, определяется во время компиляции. В динамическом полиморфизме, решается во время работы (run-time).
У вас есть кот азиатских корней (AsianCat), вы можете сказать это кот (Cat) или сказать это животное (Animal) это один из аспектов полиморфизма.

Или другой пример: В ваше резюме написано что вы азиат, хотя на самом деле вы вьетнамец. И я так же могу сказать, что вы житель Земли.

Пример ниже покажет вам как вести себя между объявлением и реальностью:
PolymorphismCatDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    class PolymorphismCatDemo
    {
        static void Main(string[] args)
        {
            // Вы объявляете объект Animal (Животное).
            // Создавая его с помощью Constructor класса Cat.
            // ** Объект 'tom' объявлен как Animal
            // поэтому он может только вызвать методы Animal.
            Animal tom = new Cat("Tom", 3, 20); 

            // Вызвать метод Sleep.
            tom.Sleep();

            // - Move() является методом определенным в классе Animal.
            // - Move() переопределяется (override) в классе Cat.
            // 'tom' на самом деле является Cat, несмотря на объявление как  Animial.
            // ==> Он вызовет метод Move() определенный в классе Cat.
            tom.Move(); // ==> Cat Move.


            Console.ReadLine();
        }
    }
}
У вас есть 2 способа для перенаписания метода определенного в родительском классе, используя ключевое слово  override или  new. И они очень разные. Посмотрим изображение ниже:
Duck.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
   class Duck : Animal
   {

       public Duck(string name)
           : base(name)
       {
           Console.WriteLine("- Duck(string)");
       }


       public new void Move()
       {
           Console.WriteLine("Duck Move..");
       }
   }
}
InheritanceDuckDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace InheritancePolymorphism
{
    class InheritanceDuckDemo
    {
        static void Main(string[] args)
        {
            // Вы объявляете объект Animal.
            // Но создаете его с помощью Constructor класса Duck.
            Animal donald = new Duck("Donald");

            // Вызвать метод Sleep() (Определенный в классе Animal).
            donald.Sleep();

            // - Move() является методом определенным в классе Animal.
            // - Move() "переписывается" с ключевым словом 'new' в классе Duck.
            //   ('new' Move() используется только для объектов объявленных как Duck или подкласса Duck).
            // 'donald' объявлен как 'Animal', не 'Duck'.
            donald.Move(); // ==> Animal Move.


            Console.ReadLine();
        }
    }
}
Запуск примера:

View more categories: