Reputation: 2105
I am developing a generic class and related generic methods. I believe the problem I am about to describe has to do with Covariance, but I can't figure out what the compiler wants me to do.
Edit: adding code which shows how I populate the dict
There is abstract base class NetworkNode. Concrete class PowerNetworkNode inherits from it. Now I have a function which will not compile. What changes should I make to get this to compile and have the desired functionality?
In abc NetworkNode, we have
public abstract IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName);
In the concrete class, PowerNetworkNode, this is overridden by
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;
}
The compiler error message is:
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?)
When I change the code to this:
public override IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
// code elided
return returnDict;
}
in which the line with "new Dictionary" now creates one with NetworkNode and not PowerNetworkNode, the compiler states
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?)
I understand what this means, but I don't know what I should change to get it to work.
Upvotes: 0
Views: 85
Reputation: 25221
You have at least two misconceptions here - even before you reach your question - which is conflating the problem.
The first issue has nothing to do with variance. Understand that this:
public override IDictionary<uint, NetworkNode>
hydrateNodes<NetworkNode>(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
Is exactly equivalent to this:
public override IDictionary<uint, NetworkNode>
hydrateNodes<T>(string nodesTableName)
{
IDictionary<uint, T> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
That is, the generic type parameter in the method name does not refer to an actual type, it just refers to what you are going to call that type in the method. If you call a method public void DoSomething<SomeClass>
, this has nothing to do with a class called SomeClass
.
It should be clear from the above that you can't assign a Dictionary<uint, PowerNetworkNode>
to a IDictionary<uint, T>
- you have no idea whether a PowerNetworkNode
is a T
.
However, if you look a little more closely, you'll realise that this method doesn't need to be generic at all - you always want T
to be a NetworkNode, so just...
public override IDictionary<uint, NetworkNode>
hydrateNodes(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, PowerNetworkNode>();
}
This is a step in the right direction but it leads to your next problem.
As you know, IDictionary<TKey, TValue>
is invariant in TValue
, so you can't assign a Dictionary<uint, PowerNetworkNode>
to a IDictionary<uint, NetworkNode>
.
Now, you've mentioned in the comments that you want "a key/value pair type that is covariant in its value."
Bad news - you can't have one of those. It's inherently a contradiction. If you declare something as Something<NetworkNode>
where you can add a NetworkNode, the compiler expects you to be able to add any NetworkNode to it. If you assign a Something<PowerNetworkNode>
to that - you've broken that contract. That's why invariance exists: because it is a logical necessity.
The good news is that you shouldn't need to - you can put a PowerNetworkNode
in an IDictionary<uint, NetworkNode>
- because a PowerNetworkNode
is a NetworkNode
.
So (unless you're doing something very strange in the rest of the method), the following should be fine:
public override IDictionary<uint, NetworkNode>
hydrateNodes(string nodesTableName)
{
IDictionary<uint, NetworkNode> returnDict =
new Dictionary<uint, NetworkNode>();
}
Hopefully this has provided some insight.
Upvotes: 3