EDIT: Now I wrote about this in more detail.
My original (and I now consider it incorrect) thought: general restrictions are not taken into account when resolving overloads and the type of output - they are used only to check the result of resolving overloads.
EDIT: Well, after a lot on this, I think I'm there. Basically my first thought was almost correct.
Constraints of a general type are only valid for removing methods from a set of candidates in a very limited set of circumstances ... in particular, only when the type of the parameter itself is general; not just a type parameter, but a generic type that uses a type type parameter. At this point, these are restrictions on parameters of the type of the general type that are checked, and not restrictions on the parameters of the type of the general method that you call.
For example:
Therefore, if you try to call Foo<object>(null) , the above method will not be part of the candidate set, because the Nullable<object> value does not satisfy the Nullable<T> constraints. If there are other applicable methods, the call can still be successful.
Now in the case above, the restrictions are the same ... but they do not have to be. For example, consider:
class Factory<TItem> where TItem : new() void Foo<T>(Factory<T> factory) where T : struct
If you try to call Foo<object>(null) , this method will still be part of the candidate set, because when TItem is object , the constraint expressed in Factory<TItem> is preserved, and this is what the candidate is checked when creating the candidate . If this proves to be the best method, he will later complete the test, towards the end of 7.6.5.1:
If the best method is a general method, type arguments (supplied or deduced) are checked for constraints (Β§4.4.4) declared in the general method. If any type argument does not satisfy the corresponding restriction (s) in the type parameter, a binding time error occurs.
Eric 's blog post contains more information about this.