いつジェネリック型制約を使用すべきか、または使用すべきではありませんか?
-
10-07-2019 - |
質問
基本クラスがあります:
public abstract class StuffBase
{
public abstract void DoSomething();
}
および2つの派生クラス
public class Stuff1 : StuffBase
{
public void DoSomething()
{
Console.WriteLine("Stuff 1 did something cool!");
}
public Stuff1()
{
Console.WriteLine("New stuff 1 reporting for duty!");
}
}
public class Stuff2 : StuffBase
{
public void DoSomething()
{
Console.WriteLine("Stuff 2 did something cool!");
}
public Stuff1()
{
Console.WriteLine("New stuff 2 reporting for duty!");
}
}
さて、今アイテムのリストを持っていると言ってください:
var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());
そして、私は彼ら全員に彼らのDoSomething()メソッドを呼び出してほしい。リストを反復して、DoSomething()メソッドを呼び出すだけなので、リストを反復してジョブを実行するAllDoSomething()というメソッドを実行するメソッドがあるとします。
public static void AllDoSomething(List<StuffBase> items)
{
items.ForEach(i => i.DoSomething());
}
次の方法の実際の違いは何ですか?
public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
items.ForEach(i => i.DoSomething());
}
両方のメソッドは、構文的には異なりますが、同じことをするために実際の用語で表示されます。
それらは同じことをする異なる方法ですか?ジェネリックと型の制約は理解していますが、このインスタンスで他のメソッドよりも一方のメソッドを使用する理由がわかりません。
解決
これは、現時点では、C#が共分散をサポートしていないためです。
より正式には、C#v2.0でTが Uのサブタイプ、T []はサブタイプ U []、ただしGはGのサブタイプではありません (Gは任意のジェネリック型です)。に 型理論用語、私たちは記述します この動作は、C#配列と言って タイプは<!>#8220; covariant <!>#8221;そしてジェネリック タイプは<!>#8220; invariant <!>#8221;です。
参照: http:// blogs。 msdn.com/rmbyers/archive/2005/02/16/375079.aspx
次の方法がある場合:
public static void AllDoSomething(List<StuffBase> items)
{
items.ForEach(i => i.DoSomething());
}
var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile
ジェネリック型制約を使用する場合と同様に、使用します。
共分散と共分散の詳細については、 Eric Lippertの一連の投稿。
読む価値のある他の投稿:
- http://www.pabich.eu/blog/archive/2008/02/12/c-generics---parameter-variance-its-constraints-and-how-it.aspx
- http://blogs.msdn.com/rmbyers /archive/2006/06/01/613690.aspx
- http://msdn.microsoft.com/ en-us / library / ms228359(VS.80).aspx
- http://www.csharp411.com/convert-between-generic- ienumerablet /
- http://research.microsoft.com/apps/pubs /default.aspx?id=64042
- リストできない理由<!> lt; parent <!> gt; = List <!> lt; child <!> gt ;?
他のヒント
リストがあるとします:
List<Stuff1> l = // get from somewhere
今すぐお試しください:
AllDoSomething(l);
汎用バージョンでは、許可されます。非ジェネリックでは、そうではありません。それが本質的な違いです。 Stuff1
のリストは、StuffBase
のリストではありません。ただし、一般的な場合、厳密に<=>のリストである必要はないため、より柔軟です。
最初に<=>のリストを<=>のリストにコピーして、非汎用バージョンとの互換性を持たせることで、この問題を回避できます。しかし、メソッドがあると仮定します:
List<T> TransformList<T>(List<T> input) where T : StuffBase
{
List<T> output = new List<T>();
foreach (T item in input)
{
// examine item and decide whether to discard it,
// make new items, whatever
}
return output;
}
ジェネリックを使用しない場合、<=>のリストを受け入れることができますが、<=>のリストを返す必要があります。呼び出し元は、アイテムが本当に派生型であることを知っている場合、キャストを使用する必要があります。そのため、ジェネリックを使用すると、引数の実際の型を保持し、メソッドを介して戻り型に渡すことができます。
指定した例では違いはありませんが、次を試してください:
List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);
最初の呼び出しは正常に機能しますが、2番目の呼び出しは一般的な共分散のためにコンパイルされません