C# &引用;“通用”;类型参数/类型参数中的继承

C# &引用;“通用”;类型参数/类型参数中的继承,c#,C#,假设我有一个函数 DoSomething(字典目录) 为什么不能称之为 DoSomething(新字典()) 由于int和string都继承自对象,因此我希望这能起作用。我知道我可以把这扩展到 DoSomething(字典目录) ,但是为什么第一个选项不起作用呢?这是因为字典不是字典 要实现这一点,类类型字典需要在TKey和TValue中都是协变的,协变需要与值类型一起工作(装箱) 首先,在.NET中,泛型类类型总是不变的。只有接口类型和委托类型可以是共变或逆变的 其次,词典在语义上不是协变的。

假设我有一个函数

DoSomething(字典目录)

为什么不能称之为

DoSomething(新字典())

由于
int
string
都继承自
对象
,因此我希望这能起作用。我知道我可以把这扩展到

DoSomething(字典目录)


,但是为什么第一个选项不起作用呢?

这是因为
字典
不是
字典

要实现这一点,类类型
字典
需要在
TKey
TValue
中都是协变的,协变需要与值类型一起工作(装箱)

首先,在.NET中,泛型类类型总是不变的。只有接口类型和委托类型可以是共变或逆变的

其次,
词典
在语义上不是协变的。例如,你可以说:

myDict.Add(new Elephant(), new BankCustomer());
如果
myDict
实际上是
Dictionary
变量中的一个(运行时)
Dictionary
,那就不太好了


现在,自.NET4.5以来,一些“非字典”类型实现了协变接口,如
IReadOnlyList
。您可能希望类似的接口
IReadOnlyDictionary
也是协变的。但事实并非如此(下面Servy的第一条评论给出了原因)。所以你没有希望。

仅仅因为
B
a
的一个子类型,并不意味着
List
List

的一个子类型,因为
Dictionary
对于两个泛型参数的类型都是不变的

在C#中,所有泛型参数都是不变的。对于接口,泛型参数只能是协变或逆变的

IDictionary
接口对于这两种类型的参数也不能是协变或逆变的。除了全部传入外,还通过
Keys
属性传入键,并且除了全部传入外,还使用
Add
传入值。因为这两种类型都用作输入和输出,所以这些类型必须是不变的

和协方差/逆方差不适用于值类型;它只能用于引用类型


仅举一个简单的例子,如果允许您进行这样的转换,那么我将能够调用
字典上的
dict.Add(“非int”,new Foo())
。我现在添加了一个不是int的键和一个不是字符串的值。因为允许强制转换将允许使用与签名不匹配的类型,所以它不起作用。

仅仅因为
字典
是不协变的

如果可能的话,那么下面的行将抛出一个运行时异常

Dictionary<object, object> dict = new Dictionary<string, string>();
dict["hello"] = new SomeOtherObject();
Dictionary dict=new Dictionary();
dict[“hello”]=新的SomeOtherObject();

由于在构建过程中不会收到警告,因此最终会出现间歇性错误。

您可以使用它,因为该类型在这种情况下不是协变或逆变的。举个例子:

var dict=new Dictionary<int,string>();
DoSomething(dict);

DoSomething(Dictionary<object, object> dict)
{
  var now=DateTime.Now;
  dict[now]=now;
}
var dict=newdictionary();
剂量测定法(dict);
DoSomething(字典dict dict)
{
var now=DateTime.now;
dict[现在]=现在;
}

在您的场景中,您现在已经在[int,string]字典中添加了一个
DateTime

IReadOnlyDictionary
必须对
TValue
保持不变,因为
TryGetValue
将其用作
out
参数,该参数不能因使用的类型而异
TKey
必须是不变的,因为该键使用
ContainsKey
作为输入,并通过
Keys
作为输出。如果
TryGetValue
的签名已更改,以致
TValue
仅用于输出,则该泛型参数可能是协变的。@Servy绝对正确!我在回答中提到了你的评论。还要注意,为了使
IReadOnlyList
T
中是协变的,他们必须省略
int IndexOf(T项)方法以其他方式出现在列表界面中。@仔细考虑后,我意识到您经常使用
IReadOnlyDictionary
来执行
foreach
。这里有一个
GetEnumerator
方法,其返回类型对于方差也很重要。为了让
foreach
发挥作用,他们需要引入一些
IReadOnlyKeyValuePair
或类似的东西。
Dictionary<object, object> dict = new Dictionary<string, string>();
dict["hello"] = new SomeOtherObject();
var dict=new Dictionary<int,string>();
DoSomething(dict);

DoSomething(Dictionary<object, object> dict)
{
  var now=DateTime.Now;
  dict[now]=now;
}