Как на самом деле кастинги работают на уровне CLR? - PullRequest
6 голосов
/ 06 мая 2010

Что происходит за кулисами при повышении или понижении? У меня была идея, что когда я делаю что-то как:

string myString = "abc";
object myObject = myString;
string myStringBack = (string)myObject;

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

.maxstack 1
.locals init (
    [0] string myString,
    [1] object myObject,
    [2] string myStringBack)
L_0000: nop 
L_0001: ldstr "abc"
L_0006: stloc.0 
L_0007: ldloc.0 
L_0008: stloc.1 
L_0009: ldloc.1 
L_000a: castclass string
L_000f: stloc.2 
L_0010: ret 

Зачем CLR нужно что-то вроде castclass string?

Существует две возможные реализации для downcast:

  1. Вам требуется castclass something. Когда вы попадаете на строку кода, которая выполняет castclass, CLR пытается выполнить приведение. Но что произошло бы, если бы я пропустил строку строки castclass и попытался запустить код?
  2. Вам не требуется castclass. Поскольку все ссылочные типы имеют одинаковую внутреннюю структуру, если вы попытаетесь использовать строку в экземпляре формы, она выдаст исключение неправильного использования (поскольку обнаруживает, что форма не является строкой или ее подтипами).

Кроме того, верно ли следующее состояние из C # 4.0 в двух словах?

Upcasting and downcasting between compatible reference types performs reference
conversions: a new reference is created that points to the same object.

Действительно ли это создает новую ссылку? Я думал, что это будет та же самая ссылка, только сохраненная в другом типе переменной.

Спасибо

Ответы [ 2 ]

14 голосов
/ 07 мая 2010

У меня была идея, что на самом деле не будет вставлен код приведения в самом коде.

Интересная идея. Как вы представили, что это сработало?

try
{
    object x = 123;
    object y = (string)x;
}
catch(InvalidCastException ex)
{ ... }

Если приведение не дает кода, тогда , где происходит код, который выдает исключение ?

Помните, что основная цель приведения типа от менее определенного к более конкретному типу - выполнить проверку типа во время выполнения .

Как только проверка типа пройдена, тогда, конечно, больше ничего не должно произойти. Биты ссылки перед проверкой типа и биты после проверки типа являются одинаковыми битами; мы только что во время выполнения проверили, оправдано ли новое использование старых битов.

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

Где это обнаруживается? Я имею в виду, в какая именно инструкция обнаружена? В инструкции CastClass . Вот для чего предназначена инструкция castclass.

что бы произошло, если бы я пропустил строку строки castclass и попытался запустить код?

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

Действительно ли это создает новую ссылку?

Помните, что на уровне реализации ссылка - это просто целое число размером с указатель. Это число, которое менеджер памяти может использовать для отслеживания позиции упомянутых данных. Это может быть указатель, это может быть ручка, не имеет значения, что это такое; это то, что реализует абстрактное понятие ссылки.

Если у вас есть переменная, которая содержит 12, и вы «заменяете» ее содержимое на 12, это «новый» 12, который только что был создан, или это «старый» 12? Предположим, вы создали вторую переменную и в нее тоже поместили 12, скопировав из первой переменной. Это «новый» 12 или «старый» 12? Как вы можете сказать? Это разница, которая не имеет значения. Когда вы создаете «новую» ссылку, идентичную «старой» ссылке, создается ли что-то новое? Вопрос философский, а не технический.

5 голосов
/ 06 мая 2010

Вы путаете ссылку с экземпляром. Создается новая ссылка , а не новый экземпляр.

object foo = "bar";
string baz = (string)foo;

Новая ссылка на строку "foo" назначена переменной baz (но есть только один экземпляр строки, просто обе переменные указывают на один экземпляр). Если бы это было не так, у вас было бы что-то похожее на «ручку» типа. Если baz и foo были буквально одной и той же ссылкой, то это ..

foo = "bim";

также сделает baz равным "bim" (аналогично, при назначении нестрокового типа baz больше не будет указывать на допустимую строковую ссылку).

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

Явное преобразование, если оно присутствует, будет иметь приоритет, даже если рассматриваемые типы совместимы без него. В случае явного преобразования у вас нет гарантии (на самом деле, это весьма маловероятно), что результат преобразования / преобразования будет указывать на тот же экземпляр, что и объект преобразования.

...