我有类似以下公共函数:
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;
}
我希望能够单独处理引用类型和可空类型。它可以编译,直到我调用值类型。对于引用类型,它可以编译。
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?)
这里真正的歧义在哪里?当T
是int
时,它不能适当地调用结构体重载吗? 另外:
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)
为什么编译器只检测引用类型的重载?我尝试着创建了两个不同的重载:
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);
}
问题仍然存在。显然,前两种方法在这里不能编译,因为重载仅仅基于约束条件是行不通的。
我尝试通过移除具有“class”约束的重载,只保留具有“struct”约束的那个,就像这样:
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 :(
我只需要更改这两个函数的名字吗?我认为有一个统一的名称是恰当的,这样调用者只需调用Get
而不必担心实现细节。
更新:如果我要避免可选参数,则 Marc 的解决方案无法使用。
mango.Get<int>(); // still wouldnt work!!
但是还有更多的魔法 :(:(
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;
}
我本来以为同样的编译器错误(在我看来)会再次困扰我。但是这一次它却能够正常工作。
Guid? g = null;
g.IsIt(); //just fine, and calls the struct constrained overload
"abcd".IsIt(); //just fine, and calls the class constrained overload
所以如果 Marc 说过载决策在约束检查之前进行,那么这次不应该也得到同样的错误吗?但是没有。为什么呢? 究竟发生了什么? :x
public static V? Get<V>(this Mango m, V defaultValue) where V : struct
,其中最后一个参数defaultValue
是一个非可空值类型,那么它将无法编译,因为我的新重载被认为具有与第一个重载相同的签名(即带有class
约束的那个)。 - Jeppe Stig Nielsen