使用动态语言运行时(DLR)安全吗?如何在DLR中更改值类型

本文关键字:DLR 类型 语言 动态 运行时 安全 | 更新日期: 2023-09-27 18:19:39

由于我最近发现了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);
}

使用动态语言运行时(DLR)安全吗?如何在DLR中更改值类型

它尽可能安全。

在引擎盖下,dynamic类型的变量实际上具有object类型。因此,当你将"String"123new Action<string>(…)分配给它时,没有什么特别的。你可能已经知道,你可以很容易地对任何object变量执行同样的操作。正在进行的唯一魔术是对值类型的值(例如123)进行装箱,但同样,这对dynamic来说并不是什么特别的事情,但自从.NET.的第一个版本以来,object x = 123;之类的赋值就已经发生了

(需要特别明确的是:当你重新分配给dyn变量时,你不会改变任何值。你只是让dyn引用一个不同的值。)

dynamic的神奇之处在于后期绑定。也就是说,每次对此类变量调用方法、属性或运算符等时,实际的方法、属性、运算符等在编译时尚不清楚;它是在运行时选择的。对于每次这样的调用,编译器都会生成代码,检查变量的当前值的类型,并尝试选择一个合适的方法、属性、运算符等进行调用。如果发现这样一个,就会调用它;否则会出现异常。

让我们看一个不同的例子:

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

第二个块抛出的异常在这里很有趣:

RuntimeBinderException:运算符*不能应用于类型为stringint的操作数

您没有得到一些算术异常,因为它甚至从未尝试将"123"乘以2。运行时一开始甚至找不到合适的*运算符来调用!运行时检查了bstring)和2int)的类型,并试图为这两种类型找到一个运算符*,但找不到任何…因此出现异常。

(当然,a * 2也发生了同样的情况;在那里,运行时看到a具有运行时类型int(因为它在那个特定时刻引用了装箱整数123),2也是如此,并且找到了两个int的运算符*;因此被调用。)

顺便说一下。,由于dynamic变量的后期绑定特性(即,只有在运行时才能对其执行适当的操作),IntelliSense(一种编译时功能)无法与它们配合使用。

在代码中,dyn包含一个引用,每次重新分配dyn时,都会重新分配该引用。如果不存在对该对象的其他引用,那么在重新分配之前引用的任何dyn现在都可以被垃圾收集。当您将值类型分配给dynamic时,该值将被装箱,因此dyn = 123将在堆上创建一个装箱的int,当重新分配dyn时,该装箱的int可以被垃圾收集。

dynamic所做的是,任何涉及dynamic变量的调用方法都是在运行时根据变量引用的对象的运行时类型来决定的,而"正常"C#中的方法是在编译时确定的,或者是简单的虚拟方法表查找。如果你试图做一些不可能的事情,你会得到一个异常,但这是在使用dynamic时预期的。

总之,dynamic变量包含一个引用,就像任何其他引用类型的变量一样。但是,使用dynamic变量时生成的代码非常不同,因为实际调用的方法只能在运行时确定。