我最近发现了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 :(得分: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
)的类型,并尝试为这两种类型找到运算符*
,但无法&# 39;找不到......因此是例外。
(当然a * 2
也是如此;运行时看到a
有运行时类型int
(因为它引用了特定的盒装整数123
({1}}以及2
的运算符*
被发现并因此被调用。)
顺便说一下,由于int
变量的后期绑定特性(即对它们的合适操作只在运行时计算出来),IntelliSense(编译时功能)不适用于它们。
答案 1 :(得分:1)
在您的代码dyn
中包含引用,每次重新分配dyn
时,您都会重新分配此引用。如果在重新分配之前引用的dyn
现在可以被垃圾收集,如果该对象不存在其他引用。将值类型分配给dynamic
时,该值将被装箱,因此dyn = 123
将在堆上创建一个装箱的int,并且在重新分配dyn
时,此装箱的int可以被垃圾收集。 / p>
dynamic
所做的是,任何涉及dynamic
变量的方法都是在运行时根据变量引用的对象的运行时类型决定的,而不是&#34 ;正常&#34; C#其中方法在编译时确定,或者是简单的虚方法表查找。如果你尝试做一些不可能的事情,你会得到一个例外,但是在使用dynamic
时会出现这种情况。
总而言之,dynamic
变量包含一个引用,就像任何其他引用类型变量一样。但是,使用dynamic
变量时生成的代码非常不同,因为调用的实际方法只能在运行时确定。