Страницы

Поиск по вопросам

среда, 5 февраля 2020 г.

Семантика работы\хранения UpCast“инга \ DownCast”инга в CLR

#c_sharp #память #clr #типы


Начнем с теории.
Допустим,имеется следующие классы:  

class A{}
class B : A{}
class C : B{}


Далее,мы делаем UpCast :  

A a1 = new C();  


Будет ли следующее утверждение верным : объект a1 является объектом типа C,и базовым
классом для него является тип А (то бишь вверх по иерархии) !?  

Далеко не уходя от кассы, представьте что добавили в код следующее: 

class A
{
    public virtual void Method()
    {
        Console.WriteLine("Method A invoked");
    }
}
class B : A
{
    public new virtual void Method()
    {
        Console.WriteLine("Method B invoked");
    }
}
class C : B
{
    public override void Method()
    {
        Console.WriteLine("Method C invoked");
    }
}


Что будет выведено на экран ?
В первую очередь покажется,что тут все очевидно,и вывод выходе получим :  

Method A invoked
Method A invoked
Method С invoked
Method C invoked   



   Но на самом то деле,мы получим : Method A invoked Method A invoked Method A invoked
Method C invoked


Исходя из этой логики,выходит что мое предыдущее высказывание не верное,и это значит,что
все таки базовым классом для C,является B ?  



Теперь перейдем к другой части вопроса.
К примеру имеем код:  

  class Program
    {
        static void Main(string[] args)
        {
            //объект типа класса А
            A a = new A();
            //объект типа класса B
            B b = new B();
            //UpCast, который равен объекту "b"
            A a1 = b;
            //UpCast как отдельный объект
            A a2 = new B();
            //DownCast, который равен объекту "а1"
            B b1 = (B)a1;

            B b2 = a as B; // вернет Null, т.к. DownCast
            //без предварительного UpCast не возможен

            // B b2 = new A(); - невозможно из за безопасности типов

            //сравниваем b с а1,видим что типы идентичны.
            Console.WriteLine(b.GetType() == a1.GetType());
            //сравниваем а2 с а1,видим что типы идентичны.
            Console.WriteLine(a2.GetType() == a1.GetType());
            //сравниваем b1 и а1,видим что типы идентичны
            Console.WriteLine(b1.GetType() == a1.GetType());

            //Проверяем сами обьекты,вернет True
            Console.WriteLine(a1.Equals(b));
            //вернет False,но реализация этих объектов идентична
            Console.WriteLine(a2.Equals(a1));
            //Вернет True
            Console.WriteLine(b1.Equals(a1));

            Console.ReadKey();
        }
    }
    class A
    {
    }
    class B : A
    {
    }  




Так все же,что происходит за кулисами?
Как при UpCast"е \ DownCast"е ,два одинаковых объекта(точнее две ссылки,указывающие
на один и тот же объект),имеют различную реализацию(да,да - это полиморфизм). За счет
чего это достигается(то бишь,как CLR реализует эту модель поведения) и как примерно
выглядит все это чудо-юдо в самой среде CLR ? Как выглядит "наследование" внутри CLR
между типами?
    


Ответы

Ответ 1



По порядку: Будет ли следующее утверждение верным : объект a1 является объектом типа C,и базовым классом для него является тип А (то бишь вверх по иерархии) !? Нет. Корректным утверждением будет следующее: объект a1 является объектом типа C,и базовыми классами для него являются типы B и А. Разница большая, поскольку каждый тип в иерархии наследования может привносить новые аспекты поведения. Теперь дальше: class A { public virtual void Method() { Console.WriteLine("Method A invoked"); } } class B : A { public new virtual void Method() { Console.WriteLine("Method B invoked"); } } class C : B { public override void Method() { Console.WriteLine("Method C invoked"); } } А данном примере сложно сказать, что именно хотел сказать автор этих строк с точки зрения бизнес-логики, но звучит это примерно так: класс B добавляет новый метод Method, но, к сожалению, он использует метод, имя которого уже есть в базовом классе. Но класс B хочет не "подменить" поведение метода из базового класса, а создать свой собстенный метод, который ничего не имеет общего с методом базового класса, кроме имени. Подобная практика приводит к неоднозначному поведению, поскольку теперь выбор метода определяется не только динамическим типом объекта (типом времени исполнения), но и типом переменной (типом, известным компилятору): если используется переменная типа A, то будет вызван метод из класса A. Если же тип переменной - это B или C, то будет использоваться другая ветка методов (ниже будет объяснение, почему это так). Другими словами, с точки зрения метода Method существует две ветки: одна начинается типом А и им же и ограничивается, и есть другая полиморфная ветка, которая начинается в типе B и продолжается в наследнике - типе С. Теперь немного о том, как это устроено в CLR. Для каждого типа CLR хранит табличку с методами (Method Table), где каждая запись описывает отельный метод - его сигнатуру и признак того, переопределяет ли данный слот метод из базового класса. Когда вы объявили метод с приставкой new в классе B, CLR добавила "новый" метод в табличку, при этом пометила этот метод, как новый, не связанный с методом из базового класса. В случае же класса С, метод Method в табличке методово типа C помечен, как полиморфный, т.е. переопределяющий поведение непосредственного базового класса. Теперь стоит сказать, как происходит разрешение метода во время исполнения: в случае вызова a.Method будет вначале определен статический тип переменной a, после чего в таблице методов будет найден метод Method. Если статический тип переменной - это A, то вначале будет просмотрена таблица методов типа A. И в этом случае CLR увидет, что этот метод виртуальный. После чего будет определен реальный тип объекта (например, тип C) и CLR посмотрит, а есть ли у этого типа переопределение метода, объявленного в типе A. CLR получит отрицательный ответ, поскольку тип C не переопределяет метод Method, объявленный в типе A (ведь этот тип переопределяет метод Method типа B). Вот и получается, что результат разрешения имени метода у нас теперь зависит не только от типа времени исполнения, но и от типа переменной времени компиляции.

Комментариев нет:

Отправить комментарий