Поскольку я недавно обнаружил DLR, я вижу, что можно легко изменять типы значений во время выполнения. Будут ли какие-либо проблемы с памятью или исключения во время выполнения? Как DLR может изменить значение без каких-либо исключений и / или ошибок памяти? Остался ли адрес объекта таким же после изменения значения? что происходит со старым значением / объектом и его адресом / ссылкой?

dynamic dyn = "String";

Console.Write(dyn);

dyn = 123;

Console.Write(dyn * 2);

dyn = new Action<string>(Test);

dyn("ABC");

static void Test(string t)
{
    Console.WriteLine(t);
}
0
111WARLOCK111 28 Авг 2014 в 12:33

2 ответа

Лучший ответ

Это так же безопасно, как и вы.

Под капотом переменная типа dynamic на самом деле имеет тип object. Так что ничего особенного не происходит, когда вы назначаете ему "String", 123 или new Action<string>(…). Возможно, вы уже знаете, что вы легко можете сделать то же самое с любой переменной object. Единственная магия, которая происходит, - это бокс для значений с типом значения (например, 123), но опять же, это не что-то особенное для dynamic, но произошло с такими присваиваниями, как object x = 123; начиная с первой версии .NET.

(Для большей ясности: вы не меняете никакого значения при повторном присвоении переменной dyn. Вы просто заставляете dyn ссылаться на другое значение.)

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

Давайте посмотрим на другой пример:

dynamic a = 123;
Console.WriteLine(a * 2); // OK

dynamic b = "123";
Console.WriteLine(b * 2); // will throw an exception

Здесь интересно исключение, выброшенное вторым блоком:

RuntimeBinderException: оператор * не может применяться к операндам типа string и int.

Вы не получили какое-то арифметическое исключение, потому что никогда даже не пытались умножить "123" на 2. Среда выполнения не смогла даже найти подходящий оператор * для вызова! Среда выполнения проверила типы b (string) и 2 (int) и попыталась найти оператор * для этих двух типов, но не смогла ' не найти… поэтому исключение.

(То же самое, конечно, произошло с a * 2; там среда выполнения увидела, что a имеет тип среды выполнения int (поскольку он ссылается на упакованное целое число 123 в тот конкретный момент) и то же самое происходит и с 2, и оператор * для двух int был найден и, таким образом, вызван.)

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

2
stakx - no longer contributing 28 Авг 2014 в 11:34

В вашем коде dyn содержит ссылку, и каждый раз, когда вы переназначаете dyn, вы переназначаете эту ссылку. Все dyn, на которые ссылались перед переназначением, теперь могут быть удалены сборщиком мусора, если на этот объект не существует других ссылок. Когда вы назначаете тип значения для dynamic, значение будет помещено в коробку, поэтому dyn = 123 создаст упакованное int в куче, а при переназначении dyn это упакованное int может быть собрано мусором.

Что делает dynamic, так это то, что любые вызываемые методы, которые включают переменную dynamic, определяются во время выполнения на основе типа времени выполнения объекта, на который ссылается переменная, в отличие от "нормального" C #, где методы определяются во время компиляции или представляют собой простой поиск в таблице виртуальных методов. И если вы попытаетесь сделать что-то невозможное, вы получите исключение, но это ожидается при использовании dynamic.

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

1
Martin Liversage 28 Авг 2014 в 08:52