关于协方差的困惑

时间:2016-12-17 16:00:24

标签: c# generics covariance

我正在开发一个泛型类和相关的泛型方法。我相信我要描述的问题与Covariance有关,但我无法弄清楚编译器要我做什么。

编辑:添加代码,显示我如何填充字典

有抽象基类NetworkNode。具体类PowerNetworkNode继承自它。现在我有一个无法编译的函数。我应该做些什么改变才能编译并拥有所需的功能?

在abc NetworkNode中,我们有

public abstract IDictionary<uint, NetworkNode> 
        hydrateNodes<NetworkNode>(string nodesTableName);

在具体的类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?)

我明白这意味着什么,但我不知道应该改变什么来让它发挥作用。

1 个答案:

答案 0 :(得分:3)

这里至少有两个误解 - 甚至在你提出问题之前 - 这使问题变得混乱。

通用类型参数名称

第一个问题与方差无关。明白这一点:

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。< / p>

但是,如果你仔细观察一下,你会发现这个方法根本不需要通用 - 你总是希望T成为一个NetworkNode,所以只是...

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>

现在,您已在评论中提到“您希望”键值/值对的类型在其值中是协变的。“

坏消息 - 你不能拥有其中之一。这本质上是一个矛盾。如果您将<{1}}声明为添加 NetworkNode,则编译器希望您能够将任何NetworkNode 添加到其中。如果您为此分配Something<NetworkNode> - 您已经违反了该合同。这就是不变性存在的原因:因为它是一种逻辑上的必然性。

新闻是您不需要的 - 您可以在Something<PowerNetworkNode>中加PowerNetworkNode - 因为IDictionary<uint, NetworkNode>PowerNetworkNode 1}}。

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

NetworkNode

希望这提供了一些见解。