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

Nev*_*ing 4 c# ienumerable foreach

我对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 )
{
}
Run Code Online (Sandbox Code Playgroud)

Raw*_*ing 6

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

B b = new B();
string str = b;
Run Code Online (Sandbox Code Playgroud)

它不能在运行时使用:

B b = new B();
object obj = b;
string str = obj; // will fail at run-time
Run Code Online (Sandbox Code Playgroud)

基本上,这是因为它是通过看远太昂贵所有可能的转换,从obj一个string可能的工作.(参见Eric Lippert博客文章).


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

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

如果改为使用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
Run Code Online (Sandbox Code Playgroud)

解决这个问题的另一种方法是制作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
Run Code Online (Sandbox Code Playgroud)

所以编译器可以在不必更改foreach循环的情况下进行转换.


Dar*_*rov 5

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

public class A
{
    public IEnumerator GetEnumerator()
    {
        yield return new B();
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以在一个foreach:

A a = new A();
foreach (B str in a)
{
    Console.WriteLine(str.GetType());
}
Run Code Online (Sandbox Code Playgroud)

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

foreach (B item in a)
{
    string str = item;
    // use the str variable here
}
Run Code Online (Sandbox Code Playgroud)