The IDictionary<TKey, TValue>
in .NET 4 / Silverlight 4 does not support covariance, i.e. I can't do a
IDictionary<string, object> myDict = new Dictionary<string, string>();
analog to what I can do with IEnumerable<T>
s now.
Probably boils down to the KeyValuePair<TKey, TValue>
not being covariant either. I feel that covariance should be allowed in dictionaries at least for the values.
So is that a bug or a feature? Will it ever come, maybe in .NET 37.4?
UPDATE (2 years later):
There will be an IReadOnlyDictionary<TKey, TValue>
in .NET 4.5, but it won't be covariant either :·/
, because it derives from IEnumerable<KeyValuePair<TKey, TValue>>
, and KeyValuePair<TKey, TValue>
is not an interface and thus cannot be covariant.
The BCL team would have to redesign a lot to come up and use some ICovariantPair<TKey, TValue>
instead. Also strongly-typed indexers á la this[TKey key]
aren't possible for covariant interfaces. A similar end can only be achieved by placing an extension method GetValue<>(this IReadOnlyDictionary<TKey, TValue> self, TKey key)
somewhere which would somehow internally have to call an an actual implementation, which arguably looks like a quite messy approach.
It's a feature. .NET 4.0 only supports safe covariance. The cast you mentioned is potentially dangerous as you could add a non-string element to the dictionary if that was possible:
IDictionary<string, object> myDict = new Dictionary<string, string>();
myDict["hello"] = 5; // not an string
On the other hand, IEnumerable<T>
is a read-only interface. The T
type parameter is only in its output positions (return type of the Current
property) so it's safe to treat IEnumerable<string>
as an IEnumerable<object>
.
But then you could say
myDict.Add("Hello, world!", new DateTime(2010, 1, 27));
which would fail miserably. The issue is that the TValue
in IDictionary<TKey, TValue>
is used in both input and output positions. To wit:
myDict.Add(key, value);
and
TValue value = myDict[key];
So is that a bug or a feature?
It's by design.
Will it ever come, maybe in .NET 37.4?
No, it's inherently unsafe.
I had a similar problem, but with more specialised derived types (rather than object which everything derives from)
The trick is to make the method generic and put a where clause putting the relevant restriction. Assuming that you're dealing with base types and derived types, the following works:
using System;
using System.Collections.Generic;
namespace GenericsTest
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
private void Run()
{
Dictionary<long, SpecialType1> a = new Dictionary<long, SpecialType1> {
{ 1, new SpecialType1 { BaseData = "hello", Special1 = 1 } },
{ 2, new SpecialType1 { BaseData = "goodbye", Special1 = 2 } } };
Test(a);
}
void Test<Y>(Dictionary<long, Y> data) where Y : BaseType
{
foreach (BaseType x in data.Values)
{
Console.Out.WriteLine(x.BaseData);
}
}
}
public class BaseType
{
public string BaseData { get; set; }
}
public class SpecialType1 : BaseType
{
public int Special1 { get; set; }
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With