C# IDictionary<&燃气轮机;相反?

C# IDictionary<&燃气轮机;相反?,c#,.net,C#,.net,我在外部类中有以下方法 public static void DoStuffWithAnimals(IDictionary<string, Animal> animals) public static void dostuff with animals(IDictionary animals) 在我的调用代码中,我已经有了一个Dictionary对象,但我不能将其作为此方法的参数传入。那么一个IDictionary不是逆变的吗?我看不出这有什么理由不起作用 我能想到的唯一解决办法

我在外部类中有以下方法

public static void DoStuffWithAnimals(IDictionary<string, Animal> animals)
public static void dostuff with animals(IDictionary animals)
在我的调用代码中,我已经有了一个
Dictionary
对象,但我不能将其作为此方法的参数传入。那么一个
IDictionary
不是逆变的吗?我看不出这有什么理由不起作用

我能想到的唯一解决办法是:

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

foreach(var kvp in lions) {
    animals.Add(kvp.Key, kvp.Value);
}
var-anives=newdictionary();
foreach(以狮子为单位的var kvp){
添加(kvp.Key,kvp.Value);
}
如果不创建相同对象的新字典,是否无法将此字典传递到此方法


编辑:


由于这是我的方法,我知道我从字典中使用的唯一成员是
TValue this[TKey key]
的getter,它是
IDictionary
的成员,因此在这种情况下,我无法对参数使用“更广泛”的类型。

假设
派生的
的子类型。然后,
Dictionary
不能是
Dictionary
的子类型,因为您不能将任何类型为
Base
的对象放入
Dictionary
中,但是
Dictionary
不能是
Dictionary
的子类型,因为如果您从
Dictionary
中获取对象,它可能不是
派生的
。因此,
Dictionary
的类型参数既不是共变的,也不是逆变的

通常,由于这个原因,可以写入的集合是不变的。如果您有一个不可变的集合,那么它可能是协变的。(如果您有某种只写的集合,它可能是相反的。)


编辑:如果您有一个“集合”,既不能从中获取数据,也不能将数据放入其中,那么它可能是协变的,也可能是逆变的。(这可能也没用。)

解决方案方面,您可以这样做,只需传入一个访问器即可:

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

    var dicLions = new Dictionary<string, Lion>();
    DoStuffWithAnimals(s => dicLions[s]);
public static void DoStuffWithAnimals(Func getAnimal)
{
}
var dicLions=新字典();
动物剂量(s=>dillions[s]);
显然,这对于您的需求来说可能有点简单,但若您只需要几个字典方法,那个么很容易实现

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

    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);
公共类访问器:IAnimalAccessor其中T:Animal
{
私人只读词典;
公共存取器(字典目录)
{
_dict=dict;
}
公共动物获取项(字符串键)
{
返回_dict[键];
}
}
公共接口IAnimalAccessor
{
动物获取项(字符串键);
}
带有动物的公共静态无效剂量(IAnimalAccessor getAnimal)
{
}
var dicLions=新字典();
var访问器=新访问器(dicLions);
动物剂量(存取器);

您可以修改
publicstaticvoidostuffwithanimals(IDictionary 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";
    }
}
void Main()
{
var=newdictionary();
添加(“一个”,新的Lion{Name=“Ben”});
动物操作器。与动物(狮子)的接触;
}
公共级动物操纵器
{
带动物的公共静态无效剂量(IDictionary animals)
T:动物
{
foreach(动物中的var kvp)
{
kvp.Value.MakeNoise();
}
}
}
公营动物
{
公共字符串名称{get;set;}
公共虚拟字符串MakeNoise()
{
返回“?”;
}
}
公营狮子:动物
{
公共重写字符串MakeNoise()
{
返回“咆哮”;
}
}

如果字典的接口是只读的(只允许您读取键值对,甚至不允许通过键值提取值),则可以使用
out
通用参数修饰符对其进行标记。如果字典的接口是只写的(允许您插入值,但不允许检索值或对其进行迭代),则可以在
通用参数修饰符中使用
标记它


因此,您可以自己创建接口并扩展Dictionary类以获得所需的功能。

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

所以你的问题实际上是关于
IDictionary

有了这一点,最简单的方法就是记住,如果类型参数的所有值要么只传入,要么只传出,那么接口只能是co/contra-variant

例如(对冲):

接口IReceiver//note'in'修饰符
{
无效添加(T项);
消除无效(T项);
}
和(协方差):

interface-IGiver//note'out'修饰符
{
T-Get(int-index);
T RemoveAt(int索引);
}
在的情况下,两个类型参数都用于
In
out
容量,这意味着接口不能是协变或逆变的。它是不变的

然而,类确实实现了协变的

这方面的一个重要参考是:

  • 我相信你想要的是协方差,而不是逆方差
  • Net中的类不能是共变或逆变的,只有接口和委托可以
  • IDictionary
    不能是协变的,因为这将允许您执行以下操作:

    IDictionary<string, Sheep> sheep = new Dictionary<string, Sheep>();
    IDictionary<string, Animal> animals = sheep;
    animals.Add("another innocent sheep", new Wolf());
    
    IDictionary sheep=new Dictionary();
    食用动物=绵羊;
    添加(“另一只无辜的羊”,新狼());
    
  • 在.NET4.5中有一个。乍一看,它可能是协变的(另一个新接口是协变的)。不幸的是,它不是,因为它还实现了
    IEnumerable
    ,并且
    KeyValuePair
    不是协变的

  • 为了解决这个问题,您可以使用类型参数的约束使您的方法成为泛型的
    interface IGiver<out T> // note 'out' modifier
    {
        T Get(int index);
        T RemoveAt(int index);
    }
    
    IDictionary<string, Sheep> sheep = new Dictionary<string, Sheep>();
    IDictionary<string, Animal> animals = sheep;
    animals.Add("another innocent sheep", new Wolf());
    
    void DoStuffWithAnimals<T>(IDictionary<string, T> animals) where T : Animal