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

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

Функция ждет тонкий объект

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

Чтобы проиллюстрировать использование ссылок базового типа в параметрах функции, заменим в предыдущем примере класс MyClass следующей модификацией (весь остальной код предыдущего примера остается неизменным)

// Вызывающая сторона
    class MyClass
    {
        public MyClass()
        {
            // Создаем объекты для всех типов 
            // иерархической цепочки наследования
            Point point = new Point(1, 2);
            Circle circle = new Circle(3, 4, 5);
            Cylinder cylinder = new Cylinder(6, 7, 8, 9);
    
            // Последовательно препарируем объекты
            Console.WriteLine("Анализируется 
    объект Point(1, 2)");
            PrepareObject(point);
    
            Console.WriteLine("\n\nАнализируется 
    объект Circle(3, 4, 5)");
            PrepareObject(circle);
    
            Console.WriteLine("\n\nАнализируется 
    объект Cylinder(6, 7, 8, 9)");
            PrepareObject(cylinder);
        }
    
        void PrepareObject(Point ob)// Функция ожидает базовый тип, поэтому 
            // ей можно передавать объекты любого производного типа
        {
            Console.WriteLine("Слой типа Point");
            ob.Show(); // Всегда есть в переданном
    
            // Безопасно проверяем, чтобы не 
       //адресоваться к несуществующему слою
            // Проверяем существование слоя Circle одним способом
            if (ob is Circle)// Выражение равно true, если слой есть
            {
                Console.WriteLine("\nСлой типа Circle");
                Circle circle = (Circle)ob;// Повышаем полномочия ссылки ob
                circle.Show();
                Console.WriteLine("Длина окружности: {0}",
       circle.Length());
                Console.WriteLine("Площадь круга: {0}", 
      circle.Area());
            }
    
            // Проверяем существование слоя Cylinder другим способом
            Cylinder cylinder = ob as Cylinder;
     // Ссылка нулевая, если слоя нет
            if (cylinder != null)
            {
                Console.WriteLine("\nСлой типа Cylinder");
                ((Cylinder)ob).Show();  
      // Повышаем полномочия ссылки ob налету
                Console.WriteLine("Площадь всей поверхности: 
      {0}", cylinder.Area());
                Console.WriteLine("Объем: {0}", 
      cylinder.Volume());
            }
        }
    }
Листинг 9.11 . Использование базовых ссылок в параметрах функций

Результат выполнения программы с модифицированным классом MyClass следующий


Чтобы не вызвать исключение времени выполнения при попытке адресации к несуществующему слою переданного объекта, мы внутри функции вначале проверяем наличие этого слоя в адресуемом объекте. Для этого используются равноценные конструкции языка с ключевыми словами as и is.

В пределе тип ссылки в аргументе функции можно объявить как object (или Object ), поскольку класс Object является базовым для всех типов без исключения. Но в этом случае внутри функции ссылку на объект Point также нужно привести к типу Point и начало функции выглядело бы так

void PrepareObject(object ob)// Функция ожидает самый базовый тип, поэтому 
            // ей можно передавать объекты любого типа
        {
            Console.WriteLine("Слой типа Point");
            ((Point)ob).Show(); // Теперь тоже нужно приводить
            ...........Остальное без изменений........... 
        }
Листинг 9.12 . Использование абсолютно базового типа

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

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

Функция ждет толстый объект

Статус ссылки на родственные объекты можно не только повышать, но и снижать для адресации к тонким слоям объекта. Вот новая модификация класса MyClass нашего примера

// Вызывающая сторона
    class MyClass
    {
        public MyClass()
        {
            // Создаем многослойный толстый объект
            Cylinder cylinder = new Cylinder(6, 7, 8, 9);
    
            // Передаем объект функции для послойной препарации
            PrepareObject(cylinder);
    
            // Тонкие объекты передавать нельзя,
            // когда функция ожидает толстый объект,
            // поскольку функция может потребовать 
            // от них несуществующих возможностей
            // PrepareObject((Circle)cylinder);// Ошибка компиляции!!!
            // PrepareObject((Point)cylinder); // Ошибка компиляции!!!
        }
    
        void PrepareObject(Cylinder ob)// Функция ожидает толстый тип
        {
            // Проверяем, что это именно толстый тип
            // if (ob is Cylinder)
            // {
            //    ...
            // }
    
            // Проверять необязательно, поскольку тоньше объекта, 
            // чем ожидается, компилятор не пропустит, а если будет 
            // передан более толстый объект, мы его приведем к исходному
            Cylinder cylinder = (Cylinder)ob; 
      // На случай, если передадут толще
    
            // Заготавливаем адреса разных слоев составного объекта,
            // снижая тем самым статус ссылки на исходный объект
            Point point = (Point)ob;
            Circle circle = (Circle)ob;
    
            // Адресуемся к членам слоя Point
            Console.WriteLine("Слой типа Point");
            point.Show();
    
            // Адресуемся к членам слоя Circle
            Console.WriteLine("\nСлой типа Circle");
            circle.Show();
            Console.WriteLine("Длина окружности: {0}", 
    circle.Length());
            Console.WriteLine("Площадь круга: {0}", 
    circle.Area());
    
            // Адресуемся к членам слоя Cylinder
            Console.WriteLine("\nСлой типа Cylinder");
            cylinder.Show();
            Console.WriteLine("Площадь всей поверхности: {0}",
    cylinder.Area());
            Console.WriteLine("Объем: {0}", 
    cylinder.Volume());
        }
    }
Листинг 9.13 . Снижение статуса ссылки для адресации к тонким слоям составного объекта

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

Результат работы программы будет такой


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

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

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

 

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

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