3

ビルドに次のようなエラーがあります。

エラー 12 型 'System.Collections.Generic.IEnumerator< BaseClass>' を 'System.Collections.Generic.IEnumerator< IParentClass>' に暗黙的に変換できません。明示的な変換が存在します (キャストがありませんか?)

単純に捨ててはダメですか?

これは私のコードです:

public Dictionary<Int32, BaseClass> Map { get; private set; }

public IEnumerator<BaseClass> GetEnumerator()
        {
            return this.Map.Values.GetEnumerator();
        }

public IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
        {
            return this.GetEnumerator(); // ERROR!
        }

私の質問は、この行を変更することはできますか?

return this.GetEnumerator();

に:

return (IEnumerator<IParentClass>)this.GetEnumerator();

(悪い副作用なしで)?

受け入れられた回答:
関数を次のように変更しました (Jon Skeet の投稿を読んだ後):

IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
        {
            return this.Map.Values.Cast<IParentClass>().GetEnumerator();
        }
4

5 に答える 5

5

現時点ではジェネリックは C# で共変ではないため、できません。.NET 自体には (デリゲートとインターフェイスの) いくつかのサポートがありますが、実際にはまだ使用されていません。

IEnumerable<BaseClass>代わりにIEnumerator<BaseClass>(そして.NET 3.5を想定して)戻ってきた場合は、使用できEnumerable.Castますが、現在は独自の拡張メソッドを記述する必要があります。

public static IEnumerator<TParent> Upcast<TParent, TChild>
    (this IEnumerator<TChild> source)
    where TChild : TParent
{
    while (source.MoveNext())
    {
        yield return source.Current;
    }
}

または、あなたの場合、以前に Cast を使用することもできます:

return this.Map.Values.Cast<BaseClass>().GetEnumerator();
于 2008-10-23T13:16:41.240 に答える
2

いいえ、できません。少なくとも C# 3.0 以下では、インターフェイスの分散はサポートされていません。これに関する Eric Lippert の優れたシリーズを参照してください。具体的には、このシリーズです

于 2008-10-23T13:16:07.730 に答える
0

いいえ、安全ではありません。以下を参照してください。

System.Collections.Generic の使用; class Foo { } class Bar : Foo { }

static class Program
{
    static IEnumerator<Foo> GetBase() {
        yield return new Foo();
        yield return new Bar();
    }
    static IEnumerator<Bar> GetDerived()
    {
        return (IEnumerator<Bar>)GetBase();
    }
    static void Main()
    {
        var obj = GetDerived(); // EXCEPTION
    }
}

ただし、反復子ブロックを使用してキャストを実行できるはずですか?

static IEnumerator<Bar> GetDerived()
{
    using (IEnumerator<Foo> e = GetBase())
    {
        while (e.MoveNext())
        {
            // or use "as" and only return valid data
            yield return (Bar)e.Current;
        }
    }
}
于 2008-10-23T13:19:17.147 に答える
0

IEnumerator<BaseClass>IEnumerator<ParentClass>は関連していませんが、それらのジェネリック パラメータは関連しています。Select代わりに、次のようなLINQ 拡張メソッドを使用します。

return this.Select(x => (IParentClass)x).GetEnumerator();

またはCast拡張メソッド:

return this.Cast<IParentClass>().GetEnumerator();
于 2008-10-23T13:15:49.197 に答える
0

これが適切でない理由を説明Enumeratorするには、 の代わりに絵を描きListます。どちらもジェネリックを使用します。コンパイラは、ジェネリック引数に関して特別な方法でどちらも処理しません。

void doStuff() {
    List<IParentThing> list = getList();
    list.add(new ChildThing2());
}

List<IParentThing> getList() {
    return new List<ChildThing1>();  //ERROR!
}

この最初の方法は問題ありません。 のリストはIParentThingを受信できるはずChildThing2です。ChildThing1しかし、 s のリストはa ChildThing2、または実際には -IParentThing以外の実装者を処理できませんChildThing1。つまり、List&lt;ChildThing1>を a としてキャストすることが許可されている場合、 andだけでなく、のすべてのサブクラスList&lt;IParent>を処理できなければなりません。IParentThingIParentThingChildThing1

Javaジェネリックには、「これが継承するもののリストが必要です」に加えて、「これを継承するもののリストが必要です」と言う方法があることに注意してください。これにより、より興味深い(そして私の意見ではエレガントな)ソリューションが可能になりますいくつかの問題に。

于 2008-10-23T13:22:59.400 に答える