foreach 如何将对象强制转换为指定类型
本文关键字:转换 类型 对象 foreach | 更新日期: 2023-09-27 17:56:56
我有一个关于C#
中foreach
行为的问题。
我的自定义类实现了自定义GetEnumerator
。此方法返回另一个可隐式转换为 string
的object
。
但是,如果我这样做foreach(string s in customClass)
它在运行时会失败("无法强制转换类型的对象......到字符串")。
但是,如果我这样做string x = new B()
它就像一个魅力。
注意:我在这里没有什么特别需要实现的,我只是想了解发生了什么。我对这种非通用行为特别感兴趣。
有什么想法吗?我缺少哪些基础知识?
复制此代码的代码:
public class A : IEnumerable
{
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
yield return new B();
}
#endregion
}
public class B
{
public static implicit operator string( B b )
{
return "to-stringed implicit";
}
}
// CODE:
A a = new A();
// Works.
B b = new B();
string xxx = b;
// Doesnt work.
foreach( string str in a )
{
}
编译器看到可以在编译时使用隐式转换时,才能使用隐式转换:
B b = new B();
string str = b;
它不能在运行时使用:
B b = new B();
object obj = b;
string str = obj; // will fail at run-time
基本上,这是因为查看从obj
到可能有效的string
的所有可能转换太昂贵了。(请参阅这篇 Eric Lippert 博客文章)。
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
循环。
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
}
C# 5.0 规范的 8.8.4)中的"foreach 语句"部分,我相信您的情况属于"枚举类型具有返回正确对象的 GetEnumerable"部分 - 没有隐式转换来确定元素的类型(如果类上没有唯一的 GetEnumerable,则有一些
)在您集合类型为 X,枚举器类型为 E,元素类型是 Current 属性的类型。
的情况下,foreach
将转换为以下代码(删除 try/final):
// 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
的结果确定,在非通用IEnumerator
的情况下Object
。如您所见,没有提及允许隐式转换的B
类型。
注意:规范可以在以下位置的正常VS C#安装中找到:"Program Files (x86)''Microsoft Visual Studio XX.0''VC#''Specification''YYYY"(XX 是 VS2010 的版本 10,VS2012 的版本 11,YYYY 是 EN-US 的区域设置 1033)。