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?
A generic method can also be overloaded by nongeneric methods. When the compiler encounters a method call, it searches for the method declaration that best matches the method name and the argument types specified in the call—an error occurs if two or more overloaded methods both could be considered best ...
So here is the list of some of the constraints that you can add to the generic classes, using the where keyword: Restrict the generic class to use the type parameter of value or reference type only (as we discussed above). Restrict the type parameter T, to be implementing the specified interface.
The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.
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>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With