7

汎用オブジェクトをList<>に追加しようとすると、エラーが発生します。

おそらく共変性と反変性に関連していますが、これを回避する方法がわかりません。where 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(block1)および同様に_blocks.Add(block2);

4

4 に答える 4

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];

何もない。_blocksはのリストなRegisterBlock<IRegister>ので、もちろん_blocks[0]タイプはRegisterBlock<IRegister>です。ただし、もちろん、リストの最初の項目は実際にはRegisterBlock<ByteRegister>です。

さて、これを止めるものは何ですか?

block1Again.AddRegister(new DoubleWordRegister())?

何もない。block1AgainはタイプRegisterBlock<IRegister>で、メソッドAddRegister(IRegister)があり、をDoubleWordRegister実装しIRegisterます。

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

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

ちなみに、ここでは1日に数回質問されることがよくあります。今朝これまでに2回:

ネストされたジェネリックインターフェイスの実装

于 2012-04-27T15:59:06.563 に答える
6

これは確かに**分散の問題です。クラスには別のインターフェースが必要になりますRegisterBlock。多分IRegisterBlock

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

次に、次のリストを作成できますIRegisterBlock

private List<IRegisterBlock> _blocks;

先週、コードベースで実際に同様の状況が発生しました。これがまさに私が解決した方法です。

于 2012-04-27T15:34:42.700 に答える
2

C#では、インターフェイスのみが共変または反変になる可能性があるため、希望する方法RegisterBlock<>で共変を明示的にマークすることはできませTん。

ただし、この場合、実際には共分散は必要ありません。2つのコレクションオブジェクトを次のように宣言する必要があります。

RegisterBlock<IRegister> block1= new RegisterBlock<IRegister>

ByteRegisterDoubleWordRegister実装の両方なので、IRegisterどちらかをに追加できますRegisterBlock<IRegister>

于 2012-04-27T15:41:42.860 に答える
0

多分あなたがそのようなことをしたなら。

ByteRegisterBlock : RegisterBlock<ByteRegister>

これでコードが機能するはずですが、柔軟性が失われます。

于 2012-04-28T00:58:16.970 に答える