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

本文关键字:转换 类型 对象 foreach | 更新日期: 2023-09-27 17:56:56

我有一个关于C#foreach行为的问题。

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

但是,如果我这样做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 )
{
}

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

只有当

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

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; ... } ,运行时转换是从 objectB(这有效,因为每个对象都是一个B),并且编译器可以进行从 Bstr 的转换。

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# 规范(

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)。