【问题标题】:Confusion about covariance对协方差的困惑
【发布时间】:2016-12-17 16:00:23
【问题描述】:

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

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

有抽象基类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;
    }

编译器指出,“新字典”行现在使用 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?)

我明白这意味着什么,但我不知道我应该改变什么才能让它工作。

【问题讨论】:

  • IDictionary&lt;TKey, TValue&gt; 不是变体,也不是TKey,也不是TValue
  • PetSerAI 答案很多。 (我不是在讽刺。)是否有任何定义为 的东西,我可以协变地使用它也是一个键值对?
  • Ant P,这会引发另一种错误。我已经更新了我的问题以涵盖发生的情况。
  • Ant P,这仍然只是将错误移动到它说的地方, returnDict[Convert.ToUInt32(oid)] = new PowerNetworkNode(oid, minPow, maxPow, edges,fuelType, ngID);现在它说错误 CS0029 无法将类型“CImodeller.Model.NetworkElements.PowerNetworkNode”隐式转换为“NetworkNode”
  • NetworkNode 应该是一个类吗?目前它是hydrateNodes 的通用参数。你应该如何填充字典?

标签: c# generics covariance


【解决方案1】:

您在这里至少有两个误解 - 甚至在您提出问题之前 - 这将问题混为一谈。

泛型类型参数名称

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

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&lt;SomeClass&gt;,这与一个名为SomeClass的类无关。

从上面应该清楚,您不能将Dictionary&lt;uint, PowerNetworkNode&gt; 分配给IDictionary&lt;uint, T&gt; - 您不知道PowerNetworkNode 是否是T

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

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

这是朝着正确方向迈出的一步,但它会导致你的下一个问题。

差异

如您所知,IDictionary&lt;TKey, TValue&gt;TValue 中是不变的,因此您不能将Dictionary&lt;uint, PowerNetworkNode&gt; 分配给IDictionary&lt;uint, NetworkNode&gt;

现在,您已经在 cmets 中提到您想要“一个其值协变的键/值对类型”。

坏消息 - 你不能拥有其中之一。本质上是矛盾的。如果您将某些内容声明为Something&lt;NetworkNode&gt;,您可以在其中添加一个网络节点,编译器希望您能够向其添加任何网络节点。如果您为其分配Something&lt;PowerNetworkNode&gt; - 您已经违反了该合同。这就是不变性存在的原因:因为它是逻辑上的必然。

好消息是您不需要这样做 - 您可以将 PowerNetworkNode 放在 IDictionary&lt;uint, NetworkNode&gt; 中 - 因为 PowerNetworkNodeNetworkNode

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

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

希望这提供了一些见解。

【讨论】:

  • 谢谢你。在仔细考虑并阅读您的回答后,我现在发现我需要从根本上重新设计我的架构。
猜你喜欢
  • 1970-01-01
  • 2020-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
  • 2011-09-24
  • 2013-07-27
  • 1970-01-01
相关资源
最近更新 更多