c# - 编译器在传递值类型时不调用适当的泛型重载

标签 c# generics type-inference overloading value-type

我有这样的公共(public)功能:

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?)

这里真正的歧义是什么?当 Tint 时,它不能适本地调用结构重载吗? 另外:

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

最佳答案

约束检查在 重载决议后完成,IIRC;重载决议似乎更喜欢第一个版本。不过,您可以强制它使用另一个:

mango.Get<int>((int?)0);

甚至:

mango.Get((int?)0);

就个人而言,我可能只是更改名称以避免歧义。

关于c# - 编译器在传递值类型时不调用适当的泛型重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14749854/

相关文章:

c# - 通用和非通用接口(interface)

java - 是否可以使泛型方法签名实现泛型+接口(interface)?

c++ - 为什么此模板参数推断失败?

Java JLS,类型的父类(super class)型集

c# - Application Insights 设置 filterContext.ExceptionHandled = TRUE

c# - 吉他标签 API?

c# - 使用 .NET 库 ECDsaCng 验证 BouncyCaSTLe ECDsa 签名

c# - 为什么我的 MVC Controller 在提供图像时速度很慢

swift - 无法覆盖泛型类的子类中的初始化程序

functional-programming - 使用 Hindley Milner 类型推断的 SML 中类型定义的增长