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