C#动态关键字 - 运行时惩罚?

Far*_*rah 16 c# performance dynamic

在C#中将实例定义为动态意味着:

  1. 编译器不执行编译时类型检查,但运行时检查就像它对所有实例一样.

  2. 编译器不执行编译时类型检查,但运行时检查发生,与任何其他非动态实例不同.

  3. 与2相同,这会带来性能损失(微不足道的?潜在的重要性?).

Eri*_*ert 40

这个问题非常令人困惑.

在C#中将实例定义为动态意味着:

通过"定义实例",您的意思是"声明变量"吗?

编译器不执行编译时类型检查,但运行时检查就像它对所有实例一样.

"像往常一样运行时检查"是什么意思?您有什么运行时检查?您是在考虑IL验证程序执行的检查,还是在考虑由强制转换引起的运行时类型检查,或者是什么?

或许最好简单解释"动态"的作用.

首先,动态距离的透视编译一个类型.从CLR的角度来看,没有动态的东西; 在代码实际运行时,所有"动态"实例都已在生成的代码中替换为"对象".

编译器将dynamic类型的表达式完全视为object类型的表达式,除了根据实例的运行时类型在运行时分析,编译和执行该表达式的值的所有操作.目标是执行的代码具有相同的语义,就好像编译器在编译时已知运行时类型一样.

你的问题似乎与性能有关.

回答性能问题的最好方法是尝试并找出 - 如果你需要硬数字你应该怎么做就是用两种方式编写代码,使用动态和使用已知类型,然后拿出秒表并比较时间.这是了解的唯一方法.

但是,让我们从抽象层面考虑某些操作的性能影响.假设你有:

int x = 123;
int y = 456;
int z = x + y;
Run Code Online (Sandbox Code Playgroud)

如今,在大多数硬件上添加两个整数大约需要十亿分之一秒.

如果我们让它变得动态会发生什么?

dynamic x = 123;
dynamic y = 456;
dynamic z = x + y;
Run Code Online (Sandbox Code Playgroud)

现在这在运行时有什么作用?这个框123和456分为对象,它们在堆上分配内存并进行一些复制.

然后它启动DLR并询问DLR"这个代码站点已经编译过一次,x和y的类型是int和int吗?"

这种情况下的答案是否定的.然后,DLR启动C#编译器的特殊版本,该编译器分析加法表达式,执行重载解析,并吐出一个描述lambda 的表达式树,它将两个int加在一起.然后,DLR将该lambda编译为动态生成的IL,然后jit编译器将其运行.然后,DLR缓存编译状态,以便第二次发出请求时,编译器不必再执行所有操作.

这需要更长的比纳秒.它可能需要数千纳秒.

这会回答你的问题吗?我真的不明白你在这里问的是什么,但我正在做出最好的猜测.

  • @AdamPlocher:从运行时的角度来看,类型“X<object>”和类型“X<dynamic>”之间没有任何区别;当我们到达运行时时,“dynamic”已被删除并替换为“object”。区别完全在于当您“使用”字典时编译器生成的代码;如果字典值类型是“object”,“d[foo].bar()”将会出错。如果它是“动态”,那么这将是合法的,并且对“bar”的调用将在运行时进行分析。 (3认同)
  • @AdamPlocher:如果您担心在对象字典中存储“int”值的拳击惩罚,那么在动态字典中存储“int”值也会受到相同的惩罚。再说一次,**动态只是戴着一顶滑稽帽子的对象**。它是“对象,但将此表达式的进一步分析推迟到运行时”。 (2认同)

Dan*_*Tao 7

据我所知,答案是3.

你可以这样做:

dynamic x = GetMysteriousObject();
x.DoLaundry();
Run Code Online (Sandbox Code Playgroud)

由于编译器没有进行类型检查x,它将编译此代码,假设您知道自己在做什么.

但这意味着必须进行额外的运行时检查:即检查x类型,查看它是否有一个DoLaundry不接受任何参数的方法,然后执行它.

换句话说,上面的代码有点像这样做(我不是说它是相同的,只是绘制一个比较):

object x = GetMysteriousObject();

MethodInfo doLaundry = x.GetType().GetMethod(
    "DoLaundry",
    BindingFlags.Instance | BindingFlags.Public
);

doLaundry.Invoke(x, null);
Run Code Online (Sandbox Code Playgroud)

这绝对不是一件轻而易举的事,但并不是说你能用肉眼看到性能问题.

相信实现dynamic涉及到为您完成的一些非常甜蜜的幕后缓存,因此如果您再次运行此代码并且x类型相同,它将运行得更快.

不过,请不要抱我这么做.我没有那么多经验dynamic; 这只是我理解它的工作方式.

  • @Braveyard:它基本上是一组标志,用于指定哪些特征适用于某个反射成员.在上面的示例中,相关特征是它是公共实例成员.例如,如果`DoLaundry`是一个静态方法,上面的代码就找不到它(你需要添加`BindingFlags.Static`). (3认同)