I have public functions like this:
public static T Get<T>(this Mango m, T defaultValue = default(T)) where T : class
{
//do something; return something;
}
public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct
{
//do something; return something;
}
Basically I want to individually handle reference types and nullable types. It compiles; until i call for value types. For reference types it compiles.
mango.Get<string>(); // compiles..
mango.Get(""); // compiles..
mango.Get<int>(); // The type 'int' must be a reference type in order to use it as
// parameter 'T' in the generic type or method Get<T>(Mango, T)
//also // The call is ambiguous between the following methods or properties:
// Get<int>(Mango, int) and Get<int>(Mango, int?)
What real ambiguity is here? When T
is int
, cant it call the struct overload appropriately? Also:
mango.Get<int>(0); // The type 'int' must be a reference type in order to use it as
// parameter 'T' in the generic type or method Get<T>(Mango, T)
Why is the compiler only detecting the reference type overload? I tried having two separate overloads:
public static T Get<T>(this Mango m) where T : class
{
return default(T);
}
public static T? Get<T>(this Mango m) where T : struct
{
return default(T);
}
public static T Get<T>(this Mango m, T def) where T : class
{
return default(T);
}
public static T? Get<T>(this Mango m, T? def) where T : struct
{
return default(T);
}
The problem persisted. And obviously, the first two methods dont compile here since overloading doesn't work merely on the basis of constraints.
I tried by removing the class
constrained overload and keeping just the struct
constrained one, like this:
public static T? Get<T>(this Mango m, T? defaultValue = default(T?)) where T : struct
{
//do something; return something;
}
mango.Get<int>(); // voila compiles!
mango.Get<int>(0); // no problem at all..
// but now I can't have mango.Get<string>() for instance :(
Am I only left with renaming the two functions? I feel its appropriate to have a unified name so that the caller just doesnt have to worry about the implementation detail, but just call Get
for any type.
Update: Marc's solution doesnt work if I have to avoid the optional parameter.
mango.Get<int>(); // still wouldnt work!!
But there's more magic :(:(
public static bool IsIt<T>(this T? obj) where T : struct
{
return who knows;
}
public static bool IsIt<T>(this T obj) where T : class
{
return perhaps;
}
By all means I'm expecting the same compiler bug (according to me) to annoy me. But no it works this time.
Guid? g = null;
g.IsIt(); //just fine, and calls the struct constrained overload
"abcd".IsIt(); //just fine, and calls the class constrained overload
So if overload resolution comes before constraint checking as Marc says, shouldn't I get the same error this time too? But no. Why is it so?? What the hell is going on? :x
constraint checking is done after overload resolution, IIRC; the overload resolution seems to prefer the first version. You can force it to use the other, though:
mango.Get<int>((int?)0);
or even:
mango.Get((int?)0);
Personally, I'd probably just change the name to avoid the ambiguity.
Interestingly, the compiler will check the constraints specified within generic types that are used within a method signature, but not for constraints within the signature itself.
Thus, if a method accepted two parameters, one of type T where T : struct
along with a Nullable<T>[]
, the compiler would not consider the method for any T
that was not a struct. The method's specified struct
constraint for T
is not considered in evaluating overloads, but the fact that Nullable<T>
constrains T
to struct, is.
I really find the total inability to consider constraints in overload evaluation bizarre, given that one could specify a default null value for the Nullable<T>[]
parameter, and pretend the parameter didn't exist. The vb.net compilers and C# compilers seem to differ, though, when it comes to what they regard as ambiguous and what they accept.
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