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? 😡
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:
or even:
Personally, I’d probably just change the name to avoid the ambiguity.