关于协方差的困惑

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> 中 - 因为 PowerNetworkNodeNetworkNode.

所以(除非你在方法的其余部分做了一些非常奇怪的事情),下面应该没问题:

public override IDictionary<uint, NetworkNode> 
    hydrateNodes(string nodesTableName)
{
    IDictionary<uint, NetworkNode> returnDict = 
         new Dictionary<uint, NetworkNode>();
}

希望这提供了一些见解。