Опубликован: 25.03.2010 | Доступ: свободный | Студентов: 1447 / 157 | Оценка: 4.31 / 4.00 | Длительность: 25:42:00
Лекция 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. Ввожу код на сайте, пишет:

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

 

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