2012-04-27 7 views
6

リストに汎用オブジェクトを追加しようとするとエラーが発生します。<>汎用タイプからインタフェースに変換できません

おそらく共分散とコントラバリャンスに関連していますが、これを回避する方法がわかりません。私はを使ってジェネリックタイプを制限しようとしましたが、ここでT:IRegister

私は、レジスタを表すためのインターフェースを持っています。次に、ByteRegisterとDoubleWordRegisterを表す2つのクラスを持っています。

public interface IRegister 
{ 
    string Name {get;set;} 
} 

public class ByteRegister : IRegister 
{ 
... 
} 

public class DoubleWordRegister : IRegister 
{ 
... 
} 

次に、同じタイプのすべてのこれらのレジスタのブロックを表す別のクラスがあります。

public class RegisterBlock<T> where T:IRegister 
{ 
    private IList<T> _registers; 

... constructors, properties etc 

    public void AddRegister(T register) 
    { 
     _registers.Add(register); 
    } 
} 

最後に、レジスタブロックとブロック内の各レジスタのリストを定義するために使用されるRegisterMapクラスがあります。私は次のエラー取得していますが

public class RegisterMap 
{ 
    private List<RegisterBlock<IRegister>> _blocks; 

    public RegisterMap() 
    { 
     _blocks = new List<RegisterBlock<IRegister>>(); 

     RegisterBlock<ByteRegister> block1= new RegisterBlock<ByteRegister>("Block1", 0); 
     block1.AddRegister(new ByteRegister("Reg1")); 
     block1.AddRegister(new ByteRegister("Reg2")); 
     _blocks.Add(block1); 

     RegisterBlock<DoubleWordRegister> block2= new RegisterBlock<DoubleWordRegister>("Block2", 10); 
     block2.AddRegister(new DoubleWordRegister("Reg3")); 
     block2.AddRegister(new DoubleWordRegister("Reg4")); 
     block2.AddRegister(new DoubleWordRegister("Reg5")); 
     _blocks.Add(block2); 
    } 
} 

:ライン上

Error 20 Argument '1': cannot convert from 'RegisterBlock<ByteRegister>' to 'RegisterBlock<IRegister>'を_blocks.Add(ブロック1)と同様に_blocks.Add上(ブロック2)。

+1

お困りですか?コンパイラのエラーはかなり明確です。 – phoog

答えて

5

これは確かに**variance problemです。あなたはRegisterBlockクラスの別のインターフェイス、多分IRegisterBlockが必要になります。

public class RegisterBlock<T> : IRegisterBlock 
    where T : IRegister 

次にあなたがIRegisterBlockのリストを作成できます。

private List<IRegisterBlock> _blocks; 

私は実際に先週私たちのコードベースで似たような状況があったが、これはまさに私がそれを解決した方法です。

+0

うわー..私は今日前に**分散問題に気づいていなかった。私は通常IEnumerable ..を使用しない傾向がありますが、今は正確にいつ使うべきかを知っています。今日は本当に重要なことを学んだと思います!私の方法はListが期待されていた Listは反変的なので、IEnumarableを使っては完璧な意味を持っています+1 – ppumkin

2

C#では共分散型または反変型型のインターフェイスしか使用できないため、に共変量を明示的に設定することはできません。

ただし、この場合には共分散を必要とする本当にをしないで、あなただけのようあなたの2つのコレクションオブジェクトを宣言する必要があります:あなたがそれらのいずれかを追加することができますIRegisterを実装DoubleWordRegister両方ByteRegisterので

RegisterBlock<IRegister> block1= new RegisterBlock<IRegister> 

と〜RegisterBlock<IRegister>

12

私は気づいたあなたは質問をすることを忘れた。あなたは単に多くの事実を述べただけです。あなたの質問は「コンパイラがこのエラーを生成するのはなぜですか?」と仮定します。

エラーが発生しないため、実行時にクラッシュする可能性があるため、コンパイラはそのエラーを生成します。私たちが許可したとします:

List<RegisterBlock<IRegister> _blocks = new List<RegisterBlock<IRegister>>(); 
RegisterBlock<ByteRegister> block1= new RegisterBlock<ByteRegister>(); 
_blocks.Add(block1); // Illegal, but suppose it was legal. 

これを中止するものは何ですか?

RegisterBlock<IRegister> block1Again = _blocks[0]; 

何もありません。_blocksRegisterBlock<IRegister>のリストですので、もちろん_blocks[0]RegisterBlock<IRegister>です。もちろん、リストの最初の項目は実際にはRegisterBlock<ByteRegister>です。

これを停止するのは何ですか?

block1Again.AddRegister(new DoubleWordRegister())? 

何もありません。 block1Againは、タイプRegisterBlock<IRegister>であり、方法はAddRegister(IRegister)であり、DoubleWordRegisterIRegisterである。

したがって、ダブルワードレジスタをバイトレジスタだけを含むブロックに配置するだけです。

明らかに安全ではありません。コンパイル時に違法にすることができる唯一の場所は、最初のステップです。共変変換は最初は合法ではありません。

ちなみに、あなたの質問は、しばしば1日に数回尋ねられます。二回、これまで今朝:たぶん

Implementing nested generic Interfaces

0

あなたのような何かをした場合。

ByteRegisterBlock : RegisterBlock<ByteRegister> 

これはコードを有効にするはずですが、柔軟性を失うことになります。

関連する問題