IDictionary <,>逆转?

Con*_*ell 17 .net c#

我在外部类中有以下方法

public static void DoStuffWithAnimals(IDictionary<string, Animal> animals)
Run Code Online (Sandbox Code Playgroud)

在我的调用代码中,我已经有了一个Dictionary<string, Lion>对象,但我无法将此作为此方法的参数传递.那么a IDictionary<,>不是逆变的吗?我看不出为什么这不起作用的任何理由.

我能想到的唯一解决方案是:

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

foreach(var kvp in lions) {
    animals.Add(kvp.Key, kvp.Value);
}
Run Code Online (Sandbox Code Playgroud)

有没有办法将此字典传递给此方法,而无需创建相同对象的新字典?


编辑:

因为这是我的方法,我知道我在字典中使用的唯一成员是getter TValue this[TKey key],它是一个成员IDictionary<TKey, TValue>,所以在这种情况下,我无法使用'更宽'类型的参数.

Dre*_*kes 8

首先,C#中的协方差和逆变仅适用于接口和委托.

所以你的问题非常重要IDictionary<TKey,TValue>.

有了这一点,最简单的方法就是记住,如果类型参数的所有值都只传入或传递出来,那么接口只能是共同变体.

例如(逆变):

interface IReceiver<in T> // note 'in' modifier
{
    void Add(T item);
    void Remove(T item);
}
Run Code Online (Sandbox Code Playgroud)

和(协方差):

interface IGiver<out T> // note 'out' modifier
{
    T Get(int index);
    T RemoveAt(int index);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下IDictionary<TKey,TValue>,两个类型参数同时用于inout容量,这意味着接口不能是协变的或逆变的.它是不变的.

但是,该类Dictionary<TKey,TValue>确实实现了IEnumerable<T>协变.

一个很好的参考是:

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

  • @Slugart,有趣的是没有.`TKey`不能是'in`,因为它是通过`Keys`属性返回的.`TValue`不能出来,因为它是`TryGetValue`的参数(虽然实际上它是一个'out`参数,所以看起来不那么令人信服).另外,`IReadOnlyDictionary <TKey,TValue>`实现`IReadOnlyCollection <KeyValuePair <TKey,TValue >>`并且任何`in` /`out`修饰符也需要在那里应用,但`KeyValuePair`不是接口.总而言之,有三个原因导致它无法变异. (2认同)

And*_*ett 7

解决方案明智的是你可以做这样的事情,只需传入一个存取器:

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

    var dicLions = new Dictionary<string, Lion>();
    DoStuffWithAnimals(s => dicLions[s]);
Run Code Online (Sandbox Code Playgroud)

显然,这可能对您的需求来说有点简单,但如果您只需要一些字典方法,那么很容易实现这一点.

这是另一种让您在动物之间重复使用代码的方法:

    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);
Run Code Online (Sandbox Code Playgroud)