Reputation: 1563
Hi I am having hard time making C# type inference do what I want. I have a very specific situation where I have a lot of variables in the flavor of
ConcurrentDictionary<T, IDictionary<U, V> >
where T,U,V can be some random types like long, int or whatever.
I want to write the method that works with this types of variables - notably examines their histograms.
So I have written a method
public static IOrderedEnumerable<Tuple<int,int>> GetDictionaryHistogram<T, U, V, W>(T dictionary) where T : ConcurrentDictionary<U, IDictionary<V, W>>
{
return dictionary.Select(p => p.Value.Count)
.GroupBy(p => p)
.Select(p => new Tuple<int, int>(p.Key, p.Count()))
.OrderBy(p => p.Item1);
}
But when I try to call it, C# gives me an error that it cannot infer the types. For example on a variable of type
ConcurrentDictionary<int,IDictionary<int, int> > foo;
I get the error:
Error 118 The type arguments for method 'Auditor.AuditorHelpers.GetDictionaryHistogram(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
What did I do wrong?
Upvotes: 2
Views: 1052
Reputation: 660159
Type inference infers from arguments to formal parameter types. No inferences are ever made on constraints because constraints are not a part of the signature of a method.
In your case type inference must always fail; type inference cannot possibly infer types for U and V because they do not appear in a formal parameter type.
For about a dozen people telling me that I am wrong to believe that this rule is sensible, see the comments to my article on the subject.
Upvotes: 7
Reputation: 10323
The type parameter T
is unnecessary. You should just write
public static IOrderedEnumerable<Tuple<int,int>> GetDictionaryHistogram<U, V, W>(
ConcurrentDictionary<U, IDictionary<V, W>> dictionary)
and it should work fine.
Your type of constraint, where a parameter must be of a certain type, is useful only in a few situations. The most common one is to avoid boxing of value types that implement an interface. Compare these two methods:
public static DoSomething(ISomeInterface thing) {}
public static DoSomething<T>(T thing) where T : ISomeInterface {}
When the first method is called with a value type argument, the argument is boxed and cast to the interface type.
When the second method is called with a value type, the generic type parameter is replaced with the value type and no boxing takes place.
Upvotes: 2
Reputation: 735
I think the compiler is saying that it can't figure out the dependencies between your types.
Try doing:
public static IOrderedEnumerable<Tuple<int,int>> GetDictionaryHistogram<U, V, W>(ConcurrentDictionary<U, IDictionary<V, W>> dictionary)
{
return dictionary.Select(p => p.Value.Count)
.GroupBy(p => p)
.Select(p => new Tuple<int, int>(p.Key, p.Count()))
.OrderBy(p => p.Item1);
}
Upvotes: 1