4

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

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

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

3 回答 3

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。(请参阅此 Eric Lippert 博客文章)。


IEnumerator返回对象,因此调用foreach (string str in a)试图在运行时将 a 转换object为 a 。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; ... },则运行时转换是 from objectto B(这有效,因为每个对象都是a B),并且 from Bto的转换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

解决此问题的另一种方法是制作AimplementIEnumerable<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循环。

于 2013-01-31T16:56:46.923 回答
5

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

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

然后你可以在 a 中使用它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
}
于 2013-01-31T16:43:48.630 回答
3

基于 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(因为它由 non-generic 的情况下的结果确定enumerator.CurrentObjectIEnumerator您所见,没有提及B允许您进行隐式转换的类型。

注意:规范可以在正常的 VS C# 安装中的以下位置找到:“Program Files (x86)\Microsoft Visual Studio XX.0\VC#\Specifications\YYYY”(XX 是 VS2010 的版本 10,VS2012 和 YYYY 的版本为 11是 EN-US 的语言环境 1033)。

于 2013-01-31T17:15:04.023 回答