luckyluke
luckyluke

Reputation: 1563

C# Type inference: How to properly define constraints on method?

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

Answers (3)

Eric Lippert
Eric Lippert

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

Jeffrey Sax
Jeffrey Sax

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

jzila
jzila

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

Related Questions