I have the following code
public class TestAdaptor
{
private interface ITargetClass
{
Guid Id { get; }
string Name { get; }
}
private class MyTargetClass : ITargetClass
{
public Guid Id { get; private set; }
public string Name { get; private set; }
public MyTargetClass(MySourceClass source)
{
}
}
private class MySourceClass
{
public Guid Id { get; set; }
public string Name { get; set; }
}
private Dictionary<Guid, IEnumerable<ITargetClass>> ConvertItems(Dictionary<Guid, IEnumerable<MySourceClass>> source)
{
return source.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Select(v => new MyTargetClass(v)));
}
}
However this will not compile as the ToDictionary line causes the following error
Cannot implicitly convert type
'System.Collections.Generic.Dictionary<System.Guid,System.Collections.Generic.IEnumerable<TestAdaptor.TestAdaptor.MyTargetClass>>'
to
'System.Collections.Generic.Dictionary<System.Guid,System.Collections.Generic.IEnumerable<TestAdaptor.TestAdaptor.ITargetClass>>' ...\TestAdaptor.cs 38 20
Now it is clearly obvious that MyTargetClass implements ITargetClass but the compiler doesn't pick this up.
For now I am explicitly converting (ITargetClass)new MyTargetClass(v)
But why is this happening in the first place and is there a better way to resolve this?
The compiler wont automatically convert IEnumberable<X>
to IEnumerable<Y>
even if X : Y
because IDictionary
is not covariant. The rationale for this is discussed here: IDictionary<,> contravariance? and IDictionary<TKey, TValue> in .NET 4 not covariant
As for getting around it, like you mentioned, you'll have to cast:
With Cast extension method:
kvp => kvp.Value.Select(v => new MyTargetClass(v)).Cast<ITargetClass>()
Explicit cast:
kvp => kvp.Value.Select(v => (ITargetClass) new MyTargetClass(v))
Update:
Just to expand on this because of the confusion between IEnumerable
and IDictionary
. IEnumerable
is covariant. IDictionary
is not.
This is just fine:
IEnumerable<ITargetClass> list = new List<MyTargetClass>();
This is not:
IDictionary<object, IEnumerable<ITargetClass>> dict =
new Dictionary<object, List<MyTargetClass>>();
IDictionary
inherits from IEnumerable<KeyValuePair<TKey, TValue>>
. At issue is the KeyValuePair
which is not covariant, which makes IDictionary
not covariant.
Select only reports what is being created and not a facet of the objects type. Let the select know what you are using via the as keyword.
Select(v => new MyTargetClass(v) as ITargetClass));
It is not the compiler's job to understand an intention
of a developer, for a class may express many interfaces. One has to provide hints to the select
statement which ultimately brings it in line with the return object required.
Otherwise you can filter the elements and return an IEnumerable of the interface using OfType IEnumerable
extension to return what is required by your method.
.Select(v => new MyTargetClass(v))
.OfType<ITargetClass>()
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