1

I am trying to write a generic extension method for a dictionary of type Dictionary<TKey, IEnumerable<TValue>> that should return the instance of the IEnumerable<TValue> for a given key and if there is no entry yet for that key create a new instance of IEnumerable<TValue> and add it for that key.

public static TEnumerable GetAndEnsureEnumerableForKey<TKey, TValue, TEnumerable>(
  this Dictionary<TKey, TEnumerable> dictionary, TKey key)
    where TEnumerable : IEnumerable<TValue> 
{
  TEnumerable enumerableForKey;
  if (!dictionary.TryGetValue(key, out enumerableForKey)) 
  {
    Type enumerableType = typeof(TEnumerable);
    enumerableForKey = (TEnumerable)Activator.CreateInstance(enumerableType);
    dictionary.Add(key, enumerableForKey);
  }

  return enumerableForKey;
}

The method itself works fine, but i have a problem with the invocation of that method. Given a Dictionary<int, List<int>> intListDictionary i would expect the call intListDictionary.GetAndEnsureEnumerableForKey(sampleInt); to work just fine. However the compiler complains that it is not able to infer the type TValue from the method call and i would have to call intListDictionary.GetAndEnsureEnumerableForKey<int, int, List<int>>(sampleInt); which kind of defeats the purpose of generics for that case.

How does it come that the compiler cannot infer the type TValue when TEnumerable is constraint to be an IEnumerable<TValue> and the concrete dictionary i am calling from should know the type?

Andreas Rayo Kniep
  • 5,674
  • 2
  • 30
  • 30

1 Answers1

2

Type inferences are only made based on type arguments, not type constraints, as explained in this answer.

To achieve what you want, you can change the type constraint for TEnumerable to the non-generic IEnumerable interface:

public static TEnumerable GetAndEnsureEnumerableForKey<TKey, TEnumerable>(
    this Dictionary<TKey, TEnumerable> dictionary,
    TKey key)
    where TEnumerable : IEnumerable
{
    TEnumerable enumerableForKey;
    if (!dictionary.TryGetValue(key, out enumerableForKey))
    {
        Type enumerableType = typeof(TEnumerable);
        enumerableForKey = (TEnumerable)Activator.CreateInstance(enumerableType);
        dictionary.Add(key, enumerableForKey);
    }

    return enumerableForKey;
}

You need to add the System.Collections to make the IEnumerable interface accessible.

Vera
  • 644
  • 5
  • 10
  • Someone suggested changing the type constraint to `IEnumerable`. While this does still allow the compiler to infer the type parameters from use, it also limits the possible uses of the method to dictionaries where the key type is the same as the element type of the enumerable, which I assume is not intended. – Vera Apr 01 '20 at 20:33
  • 1
    @StepUp You need the `System.Collections` using for the `IEnumerable` interface. – Vera Apr 02 '20 at 08:06