关于协方差的困惑
Confusion about covariance
我正在开发一个泛型 class 和相关的泛型方法。我相信我要描述的问题与协方差有关,但我无法弄清楚编译器要我做什么。
编辑:添加显示我如何填充字典的代码
有抽象基础class NetworkNode。具体class PowerNetworkNode继承自它。现在我有一个无法编译的函数。我应该进行哪些更改才能使其编译并具有所需的功能?
在 abc NetworkNode 中,我们有
public abstract IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName);
在具体的 class 中,PowerNetworkNode 被
覆盖
public override IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName)
{
IDictionary<uint, PowerNetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
// code elided
returnDict[Convert.ToUInt32(oid)] =
new PowerNetworkNode(oid, minPow, maxPow, edges, fuelType, ngID);
// NetworkNode doesn't have all the fields that PowerNetworkNode has.
return returnDict;
}
编译器错误信息为:
Error CS0266 Cannot implicitly convert type
'System.Collections.Generic.IDictionary<uint,
CImodeller.Model.NetworkElements.PowerNetworkNode>' to
'System.Collections.Generic.IDictionary<uint, NetworkNode>'.
An explicit conversion exists (are you missing a cast?)
当我将代码更改为:
public override IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
// code elided
return returnDict;
}
其中带有 "new Dictionary" 的行现在使用 NetworkNode 而不是 PowerNetworkNode 创建一个,编译器声明
Error CS0266 Cannot implicitly convert type
'System.Collections.Generic.Dictionary<uint,
CImodeller.Model.NetworkElements.PowerNetworkNode>' to
'System.Collections.Generic.IDictionary<uint, NetworkNode>'.
An explicit conversion exists (are you missing a cast?)
我明白这意味着什么,但我不知道我应该改变什么才能让它工作。
你在这里至少有两个误解 - 甚至在你提出问题之前 - 这混淆了问题。
通用类型参数名称
第一个问题与方差无关。明白这一点:
public override IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
完全是否等同于:
public override IDictionary<uint, NetworkNode>
hydrateNodes<T>(string nodesTableName)
{
IDictionary<uint, T> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
也就是说,方法名称中的泛型类型参数并不指代实际类型,它只是指代您要调用的类型在方法中。如果您调用方法 public void DoSomething<SomeClass>
,这与调用 SomeClass
.
的 class 无关
从上面应该很清楚,你不能将 Dictionary<uint, PowerNetworkNode>
分配给 IDictionary<uint, T>
- 你不知道 PowerNetworkNode
是否是 T
.
然而,如果你仔细观察,你会发现这个方法根本不需要通用 - 你总是希望 T
成为一个网络节点,所以只是......
public override IDictionary<uint, NetworkNode>
hydrateNodes(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
这是朝着正确方向迈出的一步,但它会导致您的下一个问题。
方差
如您所知,IDictionary<TKey, TValue>
在 TValue
中是不变的,因此您不能将 Dictionary<uint, PowerNetworkNode>
分配给 IDictionary<uint, NetworkNode>
。
现在,您已经在评论中提到您想要 "a key/value pair type that is covariant in its value."
坏消息 - 你不能拥有其中之一。本质上是矛盾的。如果你将某些东西声明为 Something<NetworkNode>
,你可以在其中 add 一个 NetworkNode,编译器希望你能够向它添加 any NetworkNode .如果您将 Something<PowerNetworkNode>
分配给它 - 您已经违反了该合同。这就是不变性存在的原因:因为它是逻辑上的必然性。
好 消息是您不需要 - 您可以将 PowerNetworkNode
放在 IDictionary<uint, NetworkNode>
中 - 因为 PowerNetworkNode
是 NetworkNode
.
所以(除非你在方法的其余部分做了一些非常奇怪的事情),下面应该没问题:
public override IDictionary<uint, NetworkNode>
hydrateNodes(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, NetworkNode>();
}
希望这提供了一些见解。
我正在开发一个泛型 class 和相关的泛型方法。我相信我要描述的问题与协方差有关,但我无法弄清楚编译器要我做什么。
编辑:添加显示我如何填充字典的代码
有抽象基础class NetworkNode。具体class PowerNetworkNode继承自它。现在我有一个无法编译的函数。我应该进行哪些更改才能使其编译并具有所需的功能?
在 abc NetworkNode 中,我们有
public abstract IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName);
在具体的 class 中,PowerNetworkNode 被
覆盖public override IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName)
{
IDictionary<uint, PowerNetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
// code elided
returnDict[Convert.ToUInt32(oid)] =
new PowerNetworkNode(oid, minPow, maxPow, edges, fuelType, ngID);
// NetworkNode doesn't have all the fields that PowerNetworkNode has.
return returnDict;
}
编译器错误信息为:
Error CS0266 Cannot implicitly convert type
'System.Collections.Generic.IDictionary<uint,
CImodeller.Model.NetworkElements.PowerNetworkNode>' to
'System.Collections.Generic.IDictionary<uint, NetworkNode>'.
An explicit conversion exists (are you missing a cast?)
当我将代码更改为:
public override IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
// code elided
return returnDict;
}
其中带有 "new Dictionary" 的行现在使用 NetworkNode 而不是 PowerNetworkNode 创建一个,编译器声明
Error CS0266 Cannot implicitly convert type
'System.Collections.Generic.Dictionary<uint,
CImodeller.Model.NetworkElements.PowerNetworkNode>' to
'System.Collections.Generic.IDictionary<uint, NetworkNode>'.
An explicit conversion exists (are you missing a cast?)
我明白这意味着什么,但我不知道我应该改变什么才能让它工作。
你在这里至少有两个误解 - 甚至在你提出问题之前 - 这混淆了问题。
通用类型参数名称
第一个问题与方差无关。明白这一点:
public override IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
完全是否等同于:
public override IDictionary<uint, NetworkNode>
hydrateNodes<T>(string nodesTableName)
{
IDictionary<uint, T> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
也就是说,方法名称中的泛型类型参数并不指代实际类型,它只是指代您要调用的类型在方法中。如果您调用方法 public void DoSomething<SomeClass>
,这与调用 SomeClass
.
从上面应该很清楚,你不能将 Dictionary<uint, PowerNetworkNode>
分配给 IDictionary<uint, T>
- 你不知道 PowerNetworkNode
是否是 T
.
然而,如果你仔细观察,你会发现这个方法根本不需要通用 - 你总是希望 T
成为一个网络节点,所以只是......
public override IDictionary<uint, NetworkNode>
hydrateNodes(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
这是朝着正确方向迈出的一步,但它会导致您的下一个问题。
方差
如您所知,IDictionary<TKey, TValue>
在 TValue
中是不变的,因此您不能将 Dictionary<uint, PowerNetworkNode>
分配给 IDictionary<uint, NetworkNode>
。
现在,您已经在评论中提到您想要 "a key/value pair type that is covariant in its value."
坏消息 - 你不能拥有其中之一。本质上是矛盾的。如果你将某些东西声明为 Something<NetworkNode>
,你可以在其中 add 一个 NetworkNode,编译器希望你能够向它添加 any NetworkNode .如果您将 Something<PowerNetworkNode>
分配给它 - 您已经违反了该合同。这就是不变性存在的原因:因为它是逻辑上的必然性。
好 消息是您不需要 - 您可以将 PowerNetworkNode
放在 IDictionary<uint, NetworkNode>
中 - 因为 PowerNetworkNode
是 NetworkNode
.
所以(除非你在方法的其余部分做了一些非常奇怪的事情),下面应该没问题:
public override IDictionary<uint, NetworkNode>
hydrateNodes(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, NetworkNode>();
}
希望这提供了一些见解。