C#4.0'dynamic'和foreach语句 [英] C# 4.0 'dynamic' and foreach statement

查看:180
本文介绍了C#4.0'dynamic'和foreach语句的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我发现之前不久,新的动态关键字不能很好地与C#的 foreach statement:

Not long time before I've discovered, that new dynamic keyword doesn't work well with the C#'s foreach statement:

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;
        }
    }
}



我希望迭代在动态变量应该完全工作,如同在编译时已知收集变量的类型。我发现第二个循环实际上是这样当编译时:

I've expected that iterating over the dynamic variable should work completely as if the type of collection variable is known at compile time. I've discovered that the second loop actually is looked like this when is compiled:

foreach (object x in (IEnumerable) /* dynamic cast */ (object) new Foo()) {
    ...
}

,每次访问x变量都会产生动态查找/转换,所以C#忽略了我在foreach语句中指定了正确的 x - 这是有点令人惊讶的我...以及,C#编译器完全忽略动态类型变量的集合可以实现 IEnumerable< T> interface!

and every access to the x variable results with the dynamic lookup/cast so C# ignores that I've specify the correct x's type in the foreach statement - that was a bit surprising for me... And also, C# compiler completely ignores that collection from dynamically typed variable may implements IEnumerable<T> interface!

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

The full foreach statement behavior is described in the C# 4.0 specification 8.8.4 The foreach statement article.

但是...在运行时完全可以实现相同的行为!可以添加额外的 CSharpBinderFlags.ForEachCast 标志,将emmited代码更正为:

But... It's perfectly possible to implement the same behavior at runtime! It's possible to add an extra CSharpBinderFlags.ForEachCast flag, correct the emmited code to looks like:

foreach (int x in (IEnumerable<int>) /* dynamic cast with the CSharpBinderFlags.ForEachCast flag */ (object) new Foo()) {
    ...
}

CSharpConvertBinder 添加一些额外的逻辑:

And add some extra logic to CSharpConvertBinder:


  • Wrap IEnumerable 集合和 IEnumerator IEnumerable< T> / IEnumerator< T>

  • 封装集合不会实现此接口。 Ienumerable< T> / IEnumerator< T> / li>
  • Wrap IEnumerable collections and IEnumerator's to IEnumerable<T>/IEnumerator<T>.
  • Wrap collections doesn't implementing Ienumerable<T>/IEnumerator<T> to implement this interfaces.

所以今天 foreach 语句迭代 code>完全不同于迭代在静态已知的集合变量,完全忽略由用户指定的类型信息。所有导致不同迭代行为( IEnumarble< T> - 实现集合的迭代行为被迭代为只有 IEnumerable ),并且迭代动态时,超过 150x 简单修复会带来更好的性能:

So today foreach statement iterates over dynamic completely different from iterating over statically known collection variable and completely ignores the type information, specified by user. All that results with the different iteration behavior (IEnumarble<T>-implementing collections is being iterated as only IEnumerable-implementing) and more than 150x slowdown when iterating over dynamic. Simple fix will results a much better performance:

foreach (int x in (IEnumerable<int>) dynamicVariable) {

但为什么我应该写这样的代码?

But why I should write code like this?

这是非常好的看到有时C#4.0 动态工作完全相同,如果类型将在编译时知道,但很遗憾看到

It's very nicely to see that sometimes C# 4.0 dynamic works completely the same if the type will be known at compile-time, but it's very sadly to see that dynamic works completely different where IT CAN works the same as statically typed code.

所以我的问题是:为什么动态 foreach over 动态不同于 foreach $ b

So my question is: why foreach over dynamic works different from foreach over anything else?

推荐答案

首先,为了解释一些被这个问题困惑的读者的背景:C#语言实际上并不要求收集一个foreach实现 IEnumerable 。相反,它要么实现 IEnumerable ,要么实现 IEnumerable 它有一个GetEnumerator方法(并且GetEnumerator方法返回一个与当前和MoveNext匹配预期模式的东西,等等)。

First off, to explain some background to readers who are confused by the question: the C# language actually does not require that the collection of a "foreach" implement IEnumerable. Rather, it requires either that it implement IEnumerable, or that it implement IEnumerable<T>, or simply that it have a GetEnumerator method (and that the GetEnumerator method returns something with a Current and MoveNext that matches the pattern expected, and so on.)

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

That might seem like an odd feature for a statically typed language like C# to have. Why should we "match the pattern"? Why not require that collections implement IEnumerable?

在泛型之前考虑世界。如果你想做一个int的集合,你必须使用IEnumerable。因此,每次调用当前将封装一个int,然后当然的调用者立即解开它回到int。这是缓慢的,并在GC上产生压力。通过使用基于模式的方法,您可以在C#1.0中创建强类型集合!

Think about the world before generics. If you wanted to make a collection of ints, you'd have to use IEnumerable. And therefore, every call to Current would box an int, and then of course the caller would immediately unbox it back to int. Which is slow and creates pressure on the GC. By going with a pattern-based approach you can make strongly typed collections in C# 1.0!

当然,没有人能实现这种模式;如果你想要一个强类型的集合,你实现 IEnumerable< T> ,你就完成了。如果一个通用类型的系统可用于C#1.0,​​不可能首先实现匹配模式功能。

Nowadays of course no one implements that pattern; if you want a strongly typed collection, you implement IEnumerable<T> and you're done. Had a generic type system been available to C# 1.0, it is unlikely that the "match the pattern" feature would have been implemented in the first place.

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

As you've noted, instead of looking for the pattern, the code generated for a dynamic collection in a foreach looks for a dynamic conversion to IEnumerable (and then does a conversion from the object returned by Current to the type of the loop variable of course.) So your question basically is "why does the code generated by use of the dynamic type as a collection type of foreach fail to look for the pattern at runtime?"

因为它不是1999年,甚至当它回到C#1.0天,使用模式的集合也几乎总是实现IEnumerable。真正的用户正在编写生产质量的C#4.0代码的概率是非常低的,它实现了一个实现模式而不是IEnumerable的集合。现在,如果你在这种情况下,好吧,这是意想不到的,我很抱歉,我们的设计没有预见到你的需要。如果您认为您的情况实际上很常见,并且我们错误地判断了它是多么罕见,请发布有关您的方案的更多详细信息,我们将考虑更改此假设的未来版本。

Because it isn't 1999 anymore, and even when it was back in the C# 1.0 days, collections that used the pattern also almost always implemented IEnumerable too. The probability that a real user is going to be writing production-quality C# 4.0 code which does a foreach over a collection that implements the pattern but not IEnumerable is extremely low. Now, if you're in that situation, well, that's unexpected, and I'm sorry that our design failed to anticipate your needs. If you feel that your scenario is in fact common, and that we've misjudged how rare it is, please post more details about your scenario and we'll consider changing this for hypothetical future versions.

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

Note that the conversion we generate to IEnumerable is a dynamic conversion, not simply a type test. That way, the dynamic object may participate; if it does not implement IEnumerable but wishes to proffer up a proxy object which does, it is free to do so.

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

In short, the design of "dynamic foreach" is "dynamically ask the object for an IEnumerable sequence", rather than "dynamically do every type-testing operation we would have done at compile time". This does in theory subtly violate the design principle that dynamic analysis gives the same result as static analysis would have, but in practice it's how we expect the vast majority of dynamically accessed collections to work.

这篇关于C#4.0'dynamic'和foreach语句的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆