Generic type parameter and Nullable method overloa

2019-02-09 10:35发布

问题:

Hi
I have this code using generic and nullable:

// The first one is for class
public static TResult With<TInput, TResult>(this TInput o, 
          Func<TInput, TResult> evaluator)
    where TResult : class
    where TInput : class

// The second one is for struct (Nullable)
public static TResult With<TInput, TResult>(this Nullable<TInput> o, 
          Func<TInput, TResult> evaluator)
    where TResult : class
    where TInput : struct

Please note the TInput constraint, one is class, the other one is struct. Then I use them in:

string s;
int? i;

// ...

s.With(o => "");
i.With(o => ""); // Ambiguos method

It cause an Ambiguos error. But I also have the another pair:

public static TResult Return<TInput, TResult>(this TInput o,
          Func<TInput, TResult> evaluator, TResult failureValue)
    where TInput : class

public static TResult Return<TInput, TResult>(this Nullable<TInput> o,
          Func<TInput, TResult> evaluator, TResult failureValue)
    where TInput : struct

This one compiles successfully

string s;
int? i;

// ...

s.Return(o => 1, 0);
i.Return(o => i + 1, 0);

I got no clues why this happen. The first one seems Ok, but compiles error. The second one ('Return') should be error if the first one is, but compiles successfully. Did I miss something?

回答1:

Constraints within the generic method are not considered while choosing an overload - they're checked after the overload has been chosen.

Constraints within the types of the parameters are checked as part of choosing an overload. It's a bit confusing, but it makes sense eventually.

I have a blog post on this which may help to understand it further.

Additionally note that your second example has the additional argument which contributes to type inference, which is what makes the difference between the two. TResult is inferred to be int, which prevents the first overload from being valid - there's no conversion from (int? x) => x + 1 to Func<int?, int> whereas there is a conversion from (int x) => x + 1 to Func<int, int>.