正如我在 cmets 中提到的,我总是更喜欢使用类型安全的场景而不是不使用类型安全的场景。你真正想做的是:
public T GetCookie<T>(string key)
{
return default(T);
}
public int GetCookie<int>(string key)
{
return -1;
}
...不幸的是,这在 C# 中不起作用(有很好的理由)。
如果您需要返回一个字符串,@xanatos 的解决方案是 IMO 唯一正确的解决方案。
但是,如果您不想传递另一个参数,那么最好的办法是使用重载决议来选择正确的成员:
class SimpleOverloadTest
{
private static T GetCookie<T>(string key, T value)
{
return value;
}
private static int GetCookie(string key, int value)
{
return -1;
}
static void Main()
{
Console.WriteLine(GetCookie("foo", default(int)));
Console.WriteLine(GetCookie("foo", default(float)));
Console.ReadLine();
}
}
请注意,如果您添加以下内容,这将不起作用:
private static T GetCookie<T>(string key)
{
return GetCookie(key, default(T));
}
这样做的原因是重载应该在编译时确定,并且简单地说 - 在这种情况下,选择的重载是带有泛型参数的重载 - 在我们的例子中是错误的。
通用专业化
我将@xanatos 的问题解释为“为什么 C# 不支持泛型专业化?”。我很久以前在编译器团队的一篇文章中读到过它,但现在找不到了。所以我会尽我所能......不过,像 Eric Lippert 这样的人可能会在这个问题上给出比我更好的答案......这里是:
泛型类型在 C# 编译时不会扩展。方法是编译器作为方法,具有唯一标记;你基本上调用了一个方法令牌。这是 .NET 从一开始的基础之一:您按方法编译代码。这也意味着重载决议可能发生在“C# 编译时”,而不是 JIT 时间,从而使 JIT 更快。
我认为不支持 .NET 中的通用专业化的选择主要是(运行时/JIT/编译器)速度的选择。
如果我们要实现泛型重载,我们将不得不在 C# 编译期间为每个方法标记存储多个方法实现,这将破坏上述基础。然后,您可以将具有相同方法标记的每组方法编译为不同的(汇编器)实现。在这一点上,你必须意识到这会给 JIT'ter 带来很大的压力:JIT 必须执行某种重载决议来选择“正确”的方法; C# 编译器可以帮助 JIT'ter,因为它只会生成方法。仅这一点就已经是不这样做的一个很好的理由 - 但让我们继续:
具有讽刺意味的是,模板扩展基本上就是它在 JIT 中的工作方式。如果您有值类型,则将值类型放入项目“T”中,并将方法“真正”扩展为不同的 IL 和汇编程序。换句话说:如果您有Foo<T>,那么Foo<int> 将具有与Foo<double> 不同的IL 和汇编代码。这不是引用类型的情况,因为可以对所有调用进行相同的 vtable 查找(接口约束)。换句话说,代码是为reference types 共享的,而不是为value types 共享的。您实际上可以看到这种情况发生在here。
通过这样做,您的 JIT 能够非常快速地生成非常好的代码。由于很多泛型是引用类型,它也从泛化规则中受益匪浅,这使得它流血得很快。但是,如果您将引用类型专门化,它会破坏这种优化 - 同样,这是一个不这样做的好理由。
因此,这使我们仅对值类型进行了泛型专业化。此时,你必须意识到值类型可以由其他值类型组成,这意味着我们可以递归地组合类型。在 C++ 中,我们将这些东西称为 type-lists(google for:模板元编程),它们是扩展的编译时。在 C# 中,这意味着(由于上述原因)类型列表在 JIT 阶段被扩展,并且可能发生内联,因为值类型不支持继承。这会给 JIT 编译器带来巨大的压力,而且实现起来也更加复杂。
总而言之,是的,这是一个选择,但我相信如果添加此功能,基于 JIT 的性能和复杂性,这是一个非常平衡的选择。
所以我们现在知道为什么 .NET 不支持泛型特化。然而,另一种更简单的解决方案是可能的:就像 async 和 yield 不要使用延续,你也可以使用 f.ex。 Dictionary 或 dynamic 在不破坏任何东西的情况下执行实际的重载解决方案。这不会破坏 .NET,它只会引入一些辅助方法/类。我想您当然可以在MS Connect 上将其作为功能请求提出。