今日、私は困惑したコンパイルの問題で走った。これら2つのコンテナクラスを考えてみましょう。C#コンパイラのジェネリック、継承、および失敗したメソッドの解決
public class BaseContainer<T> : IEnumerable<T>
{
public void DoStuff(T item) { throw new NotImplementedException(); }
public IEnumerator<T> GetEnumerator() { }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { }
}
public class Container<T> : BaseContainer<T>
{
public void DoStuff(IEnumerable<T> collection) { }
public void DoStuff <Tother>(IEnumerable<Tother> collection)
where Tother: T
{
}
}
前者定義DoStuff(T item)
特にDoStuff <Tother>(IEnumerable<Tother>)
後者オーバーロードは(4までIが聞く)C位のcovariance/contravarianceの不在を回避します。
このコード
Container<string> c = new Container<string>();
c.DoStuff("Hello World");
はかなり奇妙なコンパイルエラーに当たります。メソッド呼び出しから<char>
が存在しないことに注意してください。
汎用型またはメソッド 'Container.DoStuff(System.Collections.Generic.IEnumerable)'の型パラメータ 'Tother'として 'char'型を使用することはできません。 'char'から 'string'へのボクシング変換はありません。
基本的に、コンパイラはContainer.DoStuff<char>(IEnumerable<char>)
string
ため、実装IEnumerable<char>
にDoStuff(string)
に私の呼び出しをジャムではなく、BaseContainer.DoStuff(string)
を使用しようとしています。
私はこのコンパイルをするために見つけた唯一の方法は、派生クラスなぜコンパイラは1)それはそれができる知っているIEnumerable<char>
」として文字列をジャムしようとしている
public class Container<T> : BaseContainer<T>
{
public new void DoStuff(T item) { base.DoStuff(item); }
public void DoStuff(IEnumerable<T> collection) { }
public void DoStuff <Tother>(IEnumerable<Tother> collection)
where Tother: T
{
}
}
にDoStuff(T)
を追加することですt(コンパイルエラーがあるとします)、2)コンパイルがうまくいった基本クラスのメソッドがありますか? C#のジェネリックスや仮想メソッドに関することを誤解していますか? をContainer
に追加する以外の方法がありますか?
を使用してからあなたを停止しているもの、私は、これは奇妙なようだ同意するが、それは仕様に応じて正しいです。これは、2つのルールの相互作用の結果です。(1)オーバーロード解決の適用可能性検査は制約チェックの前に行われます。(2)派生クラスの適用可能なメソッドは基本クラスの適用可能メソッドよりも常に優れています。どちらも合理的に合理的なルールです。彼らはちょうどあなたのケースで特にひどく相互作用するように起こります。 –
詳細については、7.5.5.1項を参照してください。具体的には、(1)「最適なメソッドが汎用メソッドであれば、型引数(指定または推論された)が制約と照合されます...」および(2) "候補メソッドのセットは、最も派生したタイプのメソッドだけを含むように縮小されています... " –
最終的にここでの問題は設計上の問題です。あなたは、 "DoStuff"というメソッドに "T型の単一の値を扱う"と "T型の値のシーケンスを扱う"の両方を意味するようにオーバーロードしています。これは、 "タイプT"それ自体がシーケンスであるなど、多くの点で深刻な "意図解決"問題に繋がります。この問題を回避するために、BCLの既存のコレクションクラスが慎重に設計されていることがわかります。項目を取るメソッドは "Frob"と呼ばれ、項目のシーケンスを取るメソッドは "FrobRange"と呼ばれます(例えば、リストに "Add"と "AddRange"など)。 –