我对foreach
中的C#
行为有疑问。
我的自定义类实现了自定义GetEnumerator
。此方法返回另一个可隐式转换为object
的{{1}}。
但是如果我string
它在运行时失败(“无法将类型的对象转换为字符串”)。
然而,如果我做foreach(string s in customClass)
,它就像一个魅力。
注意:我没有必要在这里实现任何目标,我只是想了解发生了什么。我对这个非 - 通用行为特别感兴趣。
有什么想法吗?我缺少什么基础知识?
复制此代码的代码:
string x = new B()
答案 0 :(得分:6)
只有在编译器看到它可以在编译时使用时,才能使用隐式转换:
B b = new B();
string str = b;
它不能在运行时使用:
B b = new B();
object obj = b;
string str = obj; // will fail at run-time
基本上,这是因为通过所有查看可能有效的obj
到string
的可能转化费用太昂贵了。 (见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; ... }
,那么运行时转换是从object
到B
(这是有效的,因为每个对象 是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)。