foreach如何将对象转换为指定类型?

时间:2013-01-31 16:37:40

标签: c# foreach ienumerable

我对foreach中的C#行为有疑问。

我的自定义类实现了自定义GetEnumerator。此方法返回另一个可隐式转换为object的{​​{1}}。

但是如果我string它在运行时失败(“无法将类型的对象转换为字符串”)。

然而,如果我做foreach(string s in customClass),它就像一个魅力。

注意:我没有必要在这里实现任何目标,我只是想了解发生了什么。我对这个 - 通用行为特别感兴趣。

有什么想法吗?我缺少什么基础知识?

复制此代码的代码:

string x = new B()

3 个答案:

答案 0 :(得分:6)

只有在编译器看到它可以在编译时使用时,才能使用隐式转换:

B b = new B();
string str = b;

它不能在运行时使用:

B b = new B();
object obj = b;
string str = obj; // will fail at run-time

基本上,这是因为通过所有查看可能有效的objstring的可能转化费用太昂贵了。 (见this Eric Lippert blog post)。


您的IEnumerator会返回对象,因此调用foreach (string str in a)会在运行时尝试将object转换为B

var e = a.GetEnumerator();
e.MoveNext();
object o = e.Current;
string str = o; // will fail at run-time

如果你改为使用foreach(B item in a) { string str = item; ... },那么运行时转换是从objectB(这是有效的,因为每个对象 B ),编译器可以从B转换为str

var e = a.GetEnumerator();
e.MoveNext();
object o = e.Current;
B item = o;        // will work at run-time because o _is_ a B
string str = item; // conversion made by the compiler

解决此问题的另一种方法是A实施IEnumerable<B>而非IEnumerable。然后,foreach (string str in a)翻译为

var e = a.GetEnumerator();
e.MoveNext();
B b = e.Current; // not object!
string str = b;  // conversion made by the compiler

因此编译器可以进行转换,而无需更改foreach循环。

答案 1 :(得分:5)

foreach方法不要求您实现IEnumerable。所需要的只是您的类有一个名为GetEnumerator的方法:

public class A
{
    public IEnumerator GetEnumerator()
    {
        yield return new B();
    }
}

然后您可以在foreach

中使用它
A a = new A();
foreach (B str in a)
{
    Console.WriteLine(str.GetType());
}

但是foreach语句不会调用隐式运算符。您必须手动执行此操作:

foreach (B item in a)
{
    string str = item;
    // use the str variable here
}

答案 2 :(得分:2)

基于C#规范中的“foreach语句”部分(C#5.0规范的8.8.4)我相信你的情况属于“枚举类型具有返回适当对象的GetEnumerable”的部分 - 没有隐式转换来确定元素的类型(如果类上没有唯一的GetEnumerable,则存在一些)

  

集合类型为X,枚举器类型为E,元素类型为Current属性的类型。

foreach在您的案例中被翻译成以下代码(删除了try / finally):

// foreach( string str in items )
//    embedded-statement


IEnumerator enumerator = items.GetEnumerator();
while (enumerator.MoveNext()) 
  {
    string str = (string)(Object)enumerator.Current; // **
    embedded-statement
  }
}

请注意,在第**行上,要转换的对象类型为Object(因为它由enumerator.Current的结果确定,如果是非通用的,则为Object IEnumerator。正如您所看到的那样,您没有提及允许隐式转换的B类型。

注意:规范可以在以下位置的常规VS C#安装中找到: “程序文件(x86)\ Microsoft Visual Studio XX.0 \ VC#\规格\ YYYY”(XX是VS2010的版本10,1 VS2012的版本11,EN-US的版本1033是YYYY)。