gpt4 book ai didi

dynamic - C# 4.0 'dynamic' 和 foreach 语句

转载 作者:行者123 更新时间:2023-12-04 01:35:45 25 4
gpt4 key购买 nike

不久前我发现了新的dynamic关键字不适用于 C# 的 foreach陈述:

using System;

sealed class Foo {
public struct FooEnumerator {
int value;
public bool MoveNext() { return true; }
public int Current { get { return value++; } }
}

public FooEnumerator GetEnumerator() {
return new FooEnumerator();
}

static void Main() {
foreach (int x in new Foo()) {
Console.WriteLine(x);
if (x >= 100) break;
}

foreach (int x in (dynamic)new Foo()) { // :)
Console.WriteLine(x);
if (x >= 100) break;
}
}
}

我预计迭代 dynamic变量应该完全像在编译时知道集合变量的类型一样工作。我发现第二个循环在编译时实际上是这样的:
foreach (object x in (IEnumerable) /* dynamic cast */ (object) new Foo()) {
...
}

并且每次访问 x 变量都会导致动态查找/强制转换,因此 C# 忽略了我指定了正确的 x的 foreach 语句中的类型 - 这对我来说有点令人惊讶......而且,C# 编译器完全忽略了来自动态类型变量的集合可能实现 IEnumerable<T>界面!

完整的 foreach C# 4.0 规范 中描述了语句行为。 8.8.4 foreach 语句文章。

但是......完全有可能在运行时实现相同的行为!可以添加一个额外的 CSharpBinderFlags.ForEachCast标志,将发出的代码更正为:
foreach (int x in (IEnumerable<int>) /* dynamic cast with the CSharpBinderFlags.ForEachCast flag */ (object) new Foo()) {
...
}

并为 CSharpConvertBinder 添加一些额外的逻辑:
  • 包裹 IEnumerable收藏和IEnumeratorIEnumerable<T>/IEnumerator<T> .
  • Wrap 集合没有实现 Ienumerable<T>/IEnumerator<T>来实现这个接口(interface)。

  • 所以今天 foreach语句迭代 dynamic与迭代静态已知的集合变量完​​全不同,完全忽略用户指定的类型信息。所有这些结果都是由于不同的迭代行为( IEnumarble<T> -实现集合被迭代为仅 IEnumerable -实现)和超过 150x迭代时减速 dynamic .简单的修复将产生更好的性能:
    foreach (int x in (IEnumerable<int>) dynamicVariable) {

    但是为什么我要写这样的代码呢?

    很高兴看到有时 C# 4.0 dynamic如果在编译时知道类型,则工作方式完全相同,但很遗憾看到 dynamic在 IT CAN 与静态类型代码相同的情况下,其工作方式完全不同。

    所以我的问题是:为什么 foreach超过 dynamic作品不同于 foreach超过别的?

    最佳答案

    首先,向对这个问题感到困惑的读者解释一些背景:C#语言实际上并不要求“foreach”的集合实现IEnumerable .相反,它要求实现 IEnumerable ,或者它实现了IEnumerable<T> ,或者只是它有一个 GetEnumerator 方法(并且 GetEnumerator 方法返回的内容具有 Current 和 MoveNext 与预期的模式相匹配,依此类推。)

    对于像 C# 这样的静态类型语言来说,这似乎是一个奇怪的特性。我们为什么要“匹配模式”?为什么不要求集合实现 IEnumerable?

    想想泛型之前的世界。如果你想创建一个整数集合,你必须使用 IEnumerable。因此,对 Current 的每次调用都会将一个 int 装箱,然后调用者当然会立即将其拆箱回 int。这很慢并且会对 GC 造成压力。通过使用基于模式的方法,您可以在 C# 1.0 中创建强类型集合!

    现在当然没有人实现这种模式。如果你想要一个强类型集合,你实现 IEnumerable<T>你就完成了。如果 C# 1.0 可以使用泛型类型系统,那么“匹配模式”功能就不太可能首先实现。

    正如您所指出的,为 foreach 中的动态集合生成的代码不是寻找模式,而是寻找到 IEnumerable 的动态转换(然后将 Current 返回的对象转换为循环变量的类型当然。)所以你的问题基本上是“为什么使用动态类型作为 foreach 的集合类型生成的代码无法在运行时查找模式?”

    因为现在已经不是 1999 年了,即使回到 C# 1.0 时代,使用该模式的集合也几乎总是实现 IEnumerable。真正的用户将编写生产质量的 C# 4.0 代码,对实现该模式但不实现 IEnumerable 的集合执行 foreach 的可能性极低。现在,如果您处于这种情况,那是出乎意料的,很抱歉我们的设计未能预测到您的需求。如果您认为您的场景实际上很常见,并且我们误判了它的罕见程度,请发布有关您的场景的更多详细信息,我们将考虑为假设的 future 版本更改此内容。

    请注意,我们生成到 IEnumerable 的转换是动态转换,而不仅仅是类型测试。这样,动态对象可以参与;如果它没有实现 IEnumerable 但希望提供一个代理对象,它可以自由地这样做。

    简而言之,“动态 foreach”的设计是“动态地向对象请求一个 IEnumerable 序列”,而不是“动态地做我们在编译时会做的每一个类型测试操作”。这在理论上确实巧妙地违反了动态分析给出与静态分析相同的结果的设计原则,但在实践中,这是我们期望绝大多数动态访问的集合工作的方式。

    关于dynamic - C# 4.0 'dynamic' 和 foreach 语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2939024/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com