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.
You have at least two misconceptions here - even before you reach your question - which is conflating the problem.
Generic type parameter names
The first issue has nothing to do with variance. Understand that this:
Is exactly equivalent to this:
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 calledSomeClass
.It should be clear from the above that you can't assign a
Dictionary<uint, PowerNetworkNode>
to aIDictionary<uint, T>
- you have no idea whether aPowerNetworkNode
is aT
.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...This is a step in the right direction but it leads to your next problem.
Variance
As you know,
IDictionary<TKey, TValue>
is invariant inTValue
, so you can't assign aDictionary<uint, PowerNetworkNode>
to aIDictionary<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 aSomething<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 anIDictionary<uint, NetworkNode>
- because aPowerNetworkNode
is aNetworkNode
.So (unless you're doing something very strange in the rest of the method), the following should be fine:
Hopefully this has provided some insight.