19

外部クラスに次のメソッドがあります

public static void DoStuffWithAnimals(IDictionary<string, Animal> animals)

呼び出しコードには既にDictionary<string, Lion>オブジェクトがありますが、これをこのメソッドの引数として渡すことはできません。では、aIDictionary<,>は反変ではありませんか? これがうまくいかない理由がわかりません。

私が考えることができる唯一の解決策は次のとおりです。

var animals = new Dictionary<string, Animal>();

foreach(var kvp in lions) {
    animals.Add(kvp.Key, kvp.Value);
}

同じオブジェクトの新しい辞書を作成せずに、この辞書をこのメソッドに渡す方法はありませんか?


編集:

それが私の方法であるため、辞書から使用している唯一のメンバーTValue this[TKey key]は のメンバーであるのゲッターであることを知っているIDictionary<TKey, TValue>ため、このシナリオでは、パラメーターに「より広い」タイプを使用できません。

4

6 に答える 6

8

まず、C# の共変性と反変性は、インターフェイスとデリゲートにのみ適用されます。

だからあなたの質問は本当にについてIDictionary<TKey,TValue>です。

邪魔にならないように、型パラメーターのすべての値が渡されるか、または渡されるだけの場合にのみ、インターフェースが共変/反変になることができることを覚えておくのが最も簡単です。

例(反変性):

interface IReceiver<in T> // note 'in' modifier
{
    void Add(T item);
    void Remove(T item);
}

そして(共分散):

interface IGiver<out T> // note 'out' modifier
{
    T Get(int index);
    T RemoveAt(int index);
}

の場合IDictionary<TKey,TValue>、両方の型パラメーターがinout容量の両方で使用されます。つまり、インターフェイスは共変または反変にはなりません。不変です。

ただし、クラスは共変であるをDictionary<TKey,TValue>実装IEnumerable<T>します。

これに関する優れたリファレンスは次のとおりです。

https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

于 2012-10-11T14:25:00.560 に答える
7

解決策としては、次のようなことを行うことができます。代わりにアクセサーを渡すだけです。

    public static void DoStuffWithAnimals(Func<string, Animal> getAnimal)
    {
    }

    var dicLions = new Dictionary<string, Lion>();
    DoStuffWithAnimals(s => dicLions[s]);

明らかに、それはあなたのニーズに対して少し単純である可能性がありますが、辞書メソッドがいくつか必要な場合は、それを適切に配置するのは非常に簡単です。

これは、動物間でコードを少し再利用できるもう 1 つの方法です。

    public class Accessor<T> : IAnimalAccessor where T : Animal
    {
        private readonly Dictionary<string, T> _dict;

        public Accessor(Dictionary<string, T> dict)
        {
            _dict = dict;
        }

        public Animal GetItem(String key)
        {
            return _dict[key];
        }
    }

    public interface IAnimalAccessor
    {
        Animal GetItem(string key);
    }

    public static void DoStuffWithAnimals(IAnimalAccessor getAnimal)
    {
    }

    var dicLions = new Dictionary<string, Lion>();
    var accessor = new Accessor<Lion>(dicLions);
    DoStuffWithAnimals(accessor);
于 2012-10-11T14:17:32.650 に答える
4

Derivedが のサブタイプであるとしBaseます。次に、Dictionary<Base>タイプのDictionary<Derived>オブジェクトを に入れることができないためBaseDictionary<Derived>のサブタイプにすることはできませんが、からオブジェクトを取得すると、 ではない可能性があるためDictionary<Derived>、 のサブタイプにすることはできません。したがって、その型パラメーターでは共変でも反変でもありません。Dictionary<Base>Dictionary<Base>DerivedDictionary

一般に、書き込み可能なコレクションは、この理由で不変です。不変のコレクションがある場合、それは共変である可能性があります。(ある種の書き込み専用コレクションがある場合、それは反変である可能性があります。)

編集:そして、データを取得することもデータを入れることもできない「コレクション」がある場合、それは共変と反変の両方である可能性があります。(それも多分駄目でしょう。)

于 2012-10-11T14:10:07.987 に答える
3

public static void DoStuffWithAnimals(IDictionary<string, Animal> animals)一般的な制約を追加するように変更できます。これがLINQPadで私にとってうまくいくものです:

void Main()
{
    var lions = new Dictionary<string, Lion>();
    lions.Add("one", new Lion{Name="Ben"});
    AnimalManipulator.DoStuffWithAnimals(lions);
}

public class AnimalManipulator
{
    public static void DoStuffWithAnimals<T>(IDictionary<string, T> animals)
    where T : Animal
    {
        foreach (var kvp in animals)
        {
            kvp.Value.MakeNoise();
        }
    }
}

public class Animal
{
    public string Name {get;set;}
    public virtual string MakeNoise()
    {
        return "?";
    }
}

public class Lion : Animal
{
    public override string MakeNoise()
    {
        return "Roar";
    }
}
于 2012-10-11T14:21:15.040 に答える
2
  1. 私はあなたが望むものは共変性と呼ばれ、反変性とは呼ばれないと信じています。
  2. .Netのクラスは、共変または反変にすることはできません。インターフェースとデリゲートのみが可能です。
  3. IDictionary<TKey, TValue>共変にすることはできません。これにより、次のようなことができるようになります。

    IDictionary<string, Sheep> sheep = new Dictionary<string, Sheep>();
    IDictionary<string, Animal> animals = sheep;
    animals.Add("another innocent sheep", new Wolf());
    
  4. IReadOnlyDictionary<TKey, TValue>.Net4.5にあります。一見すると、それは共変である可能性があります(他の新しいインターフェースIReadOnlyList<T>は共変です)。残念ながら、そうではありません。これも実装されており、共変IEnumerable<KeyValuePair<TKey, TValue>>KeyValuePair<TKey, TValue>はないためです。

  5. これを回避するには、typeパラメーターの制約を使用してメソッドをジェネリックにすることができます。

    void DoStuffWithAnimals<T>(IDictionary<string, T> animals) where T : Animal
    
于 2012-10-11T14:27:41.377 に答える
0

ディクショナリのインターフェースが読み取り専用 (キーと値のペアの読み取りのみを許可し、そのキーで値を取得する機能さえ許可されていない) の場合、outジェネリック パラメーター修飾子でマークすることができます。ディクショナリのインターフェイスが書き込み専用 (値を挿入することはできますが、値を取得したり反復したりすることはできません) である場合は、inジェネリック パラメーター修飾子でマークすることができます。

そのため、必要な機能を得るために、自分でインターフェイスを作成し、Dictionary クラスを拡張できます。

于 2012-10-11T14:24:28.067 に答える