2011-12-31 14 views
4

は、以下の4つのオブジェクトが宣言ありますGenerics&Inheritance:私はここで間違っていますか?

abstract class AConfigAction {} 

abstract class APlugin<ConfigActionType> where ConfigActionType :AConfigAction {} 

class AppExecuteConfigAction : AConfigAction {} 

class AppExecutePlugin : APlugin<AppExecuteConfigAction>{} 
  • すべてのクラスが公開されています。簡単にするために、ボディは削除されています。

これはなぜ変換に失敗しますか?

_plugins = new List<APlugin<AConfigAction>>(); 
_plugins.Add(new AppExecutePlugin()); <--- Error 

'APlugin' から 'AppExecutePlugin' から変換することはできません


完全なエラーメッセージ:

エラー1にSystem.Collections「の最良のオーバーロードされたメソッドの試合.Generic.List> .Add(EnvironmentSwitcher.Model.ConfigAction.APlugin) 'に無効な引数がいくつかあります。R:\ projects \ EnvironmentSwitcher \ Environmen tSwitcher \ビュー\ ConfigurationActionManagerForm.csは35

エラー2引数が '1': 'EnvironmentSwitcher.Model.ConfigAction.APlugin R' 'にEnvironmentSwitcher.Model.ConfigAction.AppExecute.AppExecutePlugin' から変換することはできません:\プロジェクト\ EnvironmentSwitcher \ EnvironmentSwitcher \ View \ ConfigurationActionManagerForm.cs 35

答えて

22

はのは理解していること少し簡単にしてみましょう:

abstract class Animal {} // was AConfigAction 
abstract class Cage<T> where T : Animal {} // was APlugIn 
class Tiger : Animal {} // was AppExecuteConfigAction 
class TigerCage : Cage<Tiger>{} // was AppExecutePlugin 

var cages = new List<Cage<Animal>>();  
cages.Add(new TigerCage()); // Why is this an error? 

は法的たものとします。これを止めるのは何ですか?

class Shark : Animal {} // some other config action 
... 
var cages = new List<Cage<Animal>>();  
cages.Add(new TigerCage()); 
Cage<Animal> firstCage = cages[0]; 
firstCage.InsertIntoCage(new Shark()); 

firstCageは、動物の任意の種類を保持することができることを意味するタイプCage<Animal>です。しかし実際、我々はそれがトラの専用のケージであることを知っています。サメを虎の檻に入れるだけです。サメと虎の両方にとって不快なようです。

明らかにそれは許されません。何がそれを防ぐのですか?それを防ぐ唯一のことは、最初に檻の檻に虎の檻を入れるのは違法だということです。虎ケージはではありません動物ケージの一種ですので、動物ケージで出来ることがありますのでできません。虎ケージ、つまりそれにサメを入れてください。オブジェクト指向設計の基本原則は、サブタイプがスーパータイプでできることすべてを実行できることです。虎のかごは動物のかごができるすべてを行うことができないので、それはサブタイプではありません。

これは、generic型は型引数で共変することができないということです.Liskov置換原則に違反するためです。 C#4では、特定のインターフェイスとデリゲートは、型引数に共変なです。たとえば、C#4ではIEnumerable<Tiger>List<IEnumerable<Animal>>>に入れることは合法ですが、これは安全でない方法ではないからです。 IEnumerable<T>は "out-only"インターフェースなので、共分散を許容しながら置換原則を維持することができます。あなたは今までにトラを取るだけです。サメを入れる方法はありません。

+2

+1! – Odys

+0

ケージの類推の良いアイデア: – Lucero

+0

'Cage 'が抽象クラスで、 'AnimalCage:Cage {}'クラスが存在しない場合、 'firstCage'に' InsertIntoCage'をどのように呼び出すことができますか? – comecme

4

Generics covariance and contravarianceは、C#4.0でサポートされています。

abstract class AConfigAction { } 

interface APlugin<out ConfigActionType> where ConfigActionType : AConfigAction { } 

class AppExecuteConfigAction : AConfigAction { } 

class AppExecutePlugin : APlugin<AppExecuteConfigAction> { } 

class Program 
{ 
    public static void Main() 
    { 
     var _plugins = new List<APlugin<AConfigAction>>(); 
     _plugins.Add(new AppExecutePlugin()); 
    } 
} 

これはC#3.5ではサポートされていません。

+0

ありがとう、 あなたの答えには「out」とは何を表していますか? – Odys

+0

@odyodyodysでは、 'ConfigActionType'ジェネリック引数を共変変数として定義しています。つまり、このインターフェース内のメンバーはそれを返すだけで、引数として取ることはできません。 –

+0

これは.Net 4の機能だと思います(あなたとエリックの答えからわかるように)。 – Odys

関連する問題