When a method has two overloads, one accepting IDictionary
and another accepting IDictionary<TKey, TValue>
, passing new Dictionary<string, int>()
to it is considered ambigous. However, if the two overloads are changed to accept IEnumerable
and IEnumerable<KeyValuePair<TKey, TValue>>
, the call is no longer ambigous.
As Dictionary<TKey, TValue>
implements all of the above interfaces (to be precise, IDictionary<TKey, TValue>
, ICollection<KeyValuePair<TKey, TValue>>
, IDictionary
, ICollection
, IReadOnlyDictionary<TKey, TValue>
, IReadOnlyCollection<KeyValuePair<TKey, TValue>>
, IEnumerable<KeyValuePair<TKey, TValue>>
, IEnumerable
, ISerializable
, IDeserializationCallback
in .NET 4.5); as IDictionary
is inherited from IEnumerable
and IDictionary<TKey, TValue>
is inherited from IEnumerable<KeyValuePair<TKey, TValue>>
, I can't understand why it happens.
Sample console application:
using System;
using System.Collections;
using System.Collections.Generic;
namespace AmbigousCall
{
internal class Program
{
static void Main (string[] args)
{
var dic = new Dictionary<string, int>();
FooDic(dic); // Error: The call is ambiguous
FooEnum(dic); // OK: The generic method is called
Console.ReadKey();
}
static void FooDic (IDictionary dic) {}
static void FooDic<TKey, TValue> (IDictionary<TKey, TValue> dic) {}
static void FooEnum (IEnumerable dic) {}
static void FooEnum<TKey, TValue> (IEnumerable<KeyValuePair<TKey, TValue>> dic) {}
}
}
The error I get is: The call is ambiguous between the following methods or properties: 'AmbigousCall.Program.FooDic(System.Collections.IDictionary)' and 'AmbigousCall.Program.FooDic(System.Collections.Generic.IDictionary)'
Question 1: Why does it happen?
Question 2: How to accept both generic and non-generic arguments without causing ambiguity if a class implements both?
C# will call the most specific overload available. It has no trouble identifying IEnumerable<T>
as more specific than IEnumerable
because IEnumerable<T>
extends IEnumerable
. However, IDictionary<T, U>
does not extend IDictionary
, so even though Dictionary<T, U>
implements both, the compiler cannot identify which is more specific. To the compiler, these might as well be completely unrelated interfaces.
You'd have to give the compiler a hint by using an explicit cast:
FooDic((IDictionary)dic); // not ambiguous
FooDic((IDictionary<string, int>)dic); // not ambiguous
The difference is that IEnumerable<T>
inherits IEnumerable
, whereas IDictionary<TKey, TValue>
does not inherit IDictionary
.
As a result, resolving between overloads that accept IEnumerable<T>
and IEnumerable
is a simple matter of determining whether the argument matches the more specific or the more general version, whereas resolving between IDictionary
and IDictionary<TKey, TValue>
is impossible because the two interfaces are not related.
If you have overloads that accept IDictionary
and IDictionary<TKey, TValue>
, you'll have to cast your argument to the type you want:
FooDic((IDictionary)value);
or
FooDic((IDictionary<TKey, TValue>)value);
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