子クラスの列挙子を親クラスの列挙子にキャストするのは間違っていますか?
-
04-07-2019 - |
質問
ビルドでエラーが発生しました:
エラー12暗黙的に変換できません タイプ 'System.Collections.Generic.IEnumerator <!> lt; BaseClass <!> gt; ' に 'System.Collections.Generic.IEnumerator <!> lt; IParentClass <!> gt; '。 明示的な変換が存在します(あなたは キャストがありませんか?)
単純に捨てるのは間違っていますか?
これは私のコードです:
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();
to:
return (IEnumerator<IParentClass>)this.GetEnumerator();
(悪い副作用なし)?
受け入れられる回答:
関数を次のように変更しました(Jon Skeetの投稿を読んだ後):
IEnumerator<IParentClass> IEnumerable<IParentClass>.GetEnumerator()
{
return this.Map.Values.Cast<IParentClass>().GetEnumerator();
}
解決
いいえ、できません。ジェネリックは現時点では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;
}
}
代わりに、以前のキャストを使用することもできます:
return this.Map.Values.Cast<BaseClass>().GetEnumerator();
他のヒント
いいえ、できません。少なくともC#3.0以降では、インターフェイスの差異はサポートされていません。これに関するEric Lippertの優れたシリーズ、特にこれ。
いいえ、安全ではありません。以下を参照してください:
System.Collections.Genericを使用; クラスFoo {} クラス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;
}
}
}
理由なぜが適切ではないため、Enumerator
、List
ではなく画像。どちらもジェネリックを使用します-コンパイラは、ジェネリック引数に関して特別な方法でどちらも処理しません。
void doStuff() {
List<IParentThing> list = getList();
list.add(new ChildThing2());
}
List<IParentThing> getList() {
return new List<ChildThing1>(); //ERROR!
}
この最初の方法は問題ありません-IParentThing
のリストはChildThing2
を受信できるはずです。しかし、ChildThing1
のリストはList<ChildThing1>
、または実際にList<IParent>
以外の<=>のインプリメンターを処理できません。つまり、<=>が<=>としてキャストできる場合、 <=>および<=>だけでなく、<=>のすべてサブクラスを処理できる必要があります。
Javaジェネリックには、<!> quot;この<!> quot;を継承するすべてのリストが必要だと言う方法があることに注意してください。 <!> quot;に加えて、これが継承するもののリストが必要です、<!> quot;これにより、いくつかの問題に対するより興味深い(そして私の意見ではエレガントな)ソリューションが可能になります。
IEnumerator<BaseClass>
とIEnumerator<ParentClass>
は、それらの一般的なパラメーターは関係ありますが、無関係です。代わりに、次のようなLINQ Select
拡張メソッドを使用します。
return this.Select(x => (IParentClass)x).GetEnumerator();
またはCast
拡張メソッド:
return this.Cast<IParentClass>().GetEnumerator();