Опубликован: 25.03.2010 | Уровень: для всех | Доступ: платный
Лекция 9:

Наследование в C#

Полиморфные свойства ссылки базового класса

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

using System;
    
namespace Test
{
    class A
    {
    }
    
    class B
    {
    }
    
    // Вызывающий код
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = "Строгий контроль типов";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 60;
            Console.WindowHeight = 10;
   
            // Объявляем ссылки
            A obA;
            B obB;
    
            // Создаем объекты одного типа, 
            // а пытаемся адресовать их ссылками другого типа
            //obA = new B(); // Ошибка компиляции
            //obB = new A(); // Ошибка компиляции
    
            // Создаем объекты и адресуем их ссылками того же типа
            obA = new A();// Все нормально! Типы соответствуют
            obB = new B();// Все нормально! Типы соответствуют
    
            // Пробуем явно приводить типы
            //obA = (A)obB;   // Ошибка компиляции
            //obB = (B)(new A()); // Ошибка компиляции
    
            Console.ReadLine();
        }
    }
}
Листинг 9.9 . Иллюстрация механизма строгого контроля типов

Мы уже говорили, что строгий контроль типов гарантирует совместимость отдельных частей программы. Это как в детском конструкторе: из одних и тех же отдельных блоков можно собрать множество изделий при условии, что блоки будут подходить друг к другу в местах соединения. Контроль несоответствия типов позволяет легко обнаружить ошибки программирования еще на этапе компиляции - мощная и полезная вещь.

Тонкая ссылка адресует толстый объект

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


Представим себе цепочку классов, связанных одиночным наследованием, расположенную сверху вниз так, что на вершине ее находится базовый класс. Тогда ссылка, объявленная любым классом этой цепи, может адресоваться как на объект этого класса, так и на любой объект, порожденный любым нижележащим классом. Но все это в рамках своих полномочий, то есть только на члены, определенные классом объявления ссылки. Чтобы тонкая ссылка увеличила свои полномочия, ее нужно явно привести к нижележащему типу, породившему объект.

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

Итак, ссылки базового типа могут адресовать объекты производного типа (тонкие ссылки могут адресовать толстые объекты), а ссылки производного типа не могут адресовать объекты базового типа (толстые ссылки не могут адресовать тонкие объекты). Здесь мы для простоты говорим об адресации объектов, имея ввиду возможность адресации членов этих объектов (полей, свойств, методов) в вызывающем коде, когда мы постепенно будем наращивать наше приложение.

using System;
    
namespace Test
{
    class Point
    {
        protected string  name = "Точка: ";
        protected int x, y;
    
        public Point()
        {
            x = y = 0;
        }
    
        public Point(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
    
        public void Show()
        {
            Console.WriteLine(name + "x={0}; y={1}", x, y);
        }
    }
    
    class Circle : Point
    {
        new protected string name = "Круг: ";
        protected int radius;
    
        public Circle() 
        {
            radius = 1;
        }
    
        public Circle(int x, int y, int radius)
            : base(x, y)
        {
            this.radius = radius;
        }
    
        public double Length()
        {
            return 2.0 * Math.PI * radius;
        }
    
        public double Area()
        {
            return Math.PI * radius * radius;
        }
    
        new public void Show()
        {
            Console.WriteLine(name + "x={0}; y={1}; 
    r={2}", x, y, radius);
        }
    }
    
    class Cylinder : Circle
    {
        new string name = "Цилиндр: ";
        int height;// Высота цилиндра
    
        public Cylinder()
        {
            height = 0;
        }
    
        public Cylinder(int x, int y, int radius, int height)
            : base(x, y, radius)
        {
            this.height = height;
        }
    
        new public double Area()
        {
            double area = 2 * base.Area()
            + base.Length() * height;
            return area;
        }
    
        public double Volume()// Объем
        {
            return base.Area() * height;
        }
    
        new public void Show()
        {
            Console.WriteLine(name + "x={0}; y={1}; 
    r={2}; h={3}", x, y, radius, height);
        }
    }
    
    // Вызывающая сторона
    class MyClass
    {
        public MyClass()
        {
            // Создаем объекты для всех типов 
            // иерархической цепочки наследования
            // и адресуем их тонкой ссылкой
            Point[] point = {
                new Point(1, 2),
                new Circle(3, 4, 5),
                new Cylinder(6, 7, 8, 9)
            };
    
            // Распечатываем
            string str = 
            @"Point[] point = {
                new Point(1, 2),
                new Circle(3, 4, 5),
                new Cylinder(6, 7, 8, 9)
            };";
            Console.WriteLine("Вот такие мы создали 
    слоеные объекты:");
            Console.WriteLine(str);
            Console.WriteLine();
    
            Console.WriteLine("Содержимое слоя Point");
            for (int i = 0; i < point.Length; i++)
                point[i].Show();
            Console.WriteLine();
    
            Console.WriteLine("Содержимое слоя Circle");
            for (int i = 1; i < point.Length; i++)
                ((Circle)point[i]).Show();// Увеличиваем полномочия тонкой ссылки
            Console.WriteLine();
    
            Console.WriteLine("Содержимое слоя Cylinder");
            for (int i = 2; i < point.Length; i++)
                ((Cylinder)point[i]).Show();// Увеличиваем полномочия тонкой ссылки
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = "Ссылки родственных типов";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 60;
            Console.WindowHeight = 20;
    
            new MyClass();// Чтобы сработал конструктор
    
            Console.ReadLine();
        }
    }
}
Листинг 9.10 . Пример преобразования родственных ссылок

Результат выполнения будет таким


Увеличение полномочий тонких ссылок называется явным приведением типов. Вначале все объекты цепочки наследования адресовались тонкими ссылками, имеющими базовый тип. Это допустимо и при формировании массива базового типа компилятор осуществлял неявное преобразование к тонкому типу. Хоть некоторые из тонких ссылок адресовали толстые объекты, но ссылались в них только на базовый слой. Стоило нам повысить полномочия ссылки с помощью явного преобразования типов, как она становилась способной адресовать составной объект целиком.


Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

Срок действия этого кода проверки уже истек. Проверьте, правильно ли введен код. У вас осталось две попытки. Вы также можете выбрать другой способ проверки или предоставить соответствующие документы, подтверждающие ваш академический статус.

 

Как активировать код?

Денис Пашков
Денис Пашков
Россия
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974