4

foreachでの振る舞いについて質問がありC#ます。

私のカスタム クラスは custom を実装しています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

基本的に、これは からへの可能なすべての変換を調べるのはコストがかかりすぎるためです。(この Eric Lippert のブログ投稿を参照してください)。objstring


あなたIEnumeratorはオブジェクトを返すので、呼び出しは実行時に a を aforeach (string str in a)に変換しようとします。objectB

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>IEnumerableforeach (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();
    }
}

そして、あなたはそれをで使うことができます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#仕様(C#5.0仕様の8.8.4)の「foreachステートメント」セクションに基づいて、あなたのケースは「列挙型には適切なオブジェクトを返す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
  }
}

**オンラインでは、変換されるオブジェクトのタイプは( non-generic の場合のObject結果によって決定されるため) でenumerator.Currentあることに注意してください。ご覧のとおり、暗黙的な変換を可能にするタイプについての言及はありません。ObjectIEnumeratorB

注: 仕様は、次の場所にある通常の VS C# インストールで見つけることができます。 EN-US の場合はロケール 1033 です)。

于 2013-01-31T17:15:04.023 に答える