为了缩短它,我有一个看起来像这样的方法
public T GetCookie<T>(string key)
{
T obj;
if (typeof(T) == typeof(int))
{
return -1;
}
else
{
return default(T);
}
}
为什么我这样做? int
的默认值为0
,我需要它为-1
(我根据-1
得到了一个巨大的代码示例),所以我想知道是否存在可能会更改int
的默认值的内容,或者允许我返回int
而不是我的通用类。
我现在没找到任何东西:/
答案 0 :(得分:2)
尝试做类似的事情:
return (T)(object)-1;
答案 1 :(得分:2)
不,无法更改默认值。它是初始化字段时编译器分配的值。
针对您的问题的解决方案纯粹是基于意见。
如果只使用有限数量的类型,您可以使用单独的方法:
public int GetIntCookie(string key)
public string GetStringCookie<T>(string key)
public DateTime GetDateTimeCookie<T>(string key)
或者您可以维护默认值字典:
private static Dictionary<Type, object> = new Dictionary<Type, object>
{
{ typeof(int), -1 },
{ typeof(string), string.Empty },
{ typeof(DateTime), DateTime.MinValue },
}
public T GetCookie<T>(string key)
{
object value;
if (defaultValues.TryGetValue(typeof(T), out value)
{
return (T)value;
}
return default(T);
}
或者您可以保留您的实现(并修复编译器错误)
public T GetCookie<T>(string key)
{
if (typeof(T) == typeof(int))
{
return (T)(object)-1;
}
return default(T);
}
答案 2 :(得分:1)
正如我在评论中提到的,我总是更喜欢使用类型安全的场景而不是使用类型安全的场景。你真正想做的是:
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中的问题解释为&#34;为什么C#不支持通用专业化?&#34;。我很久以前就在编译器团队的一篇文章中读到了它,但是再也找不到了。所以我会从头脑中尽我所能......像Eric Lippert这样的人可能会比我更好地回答这个问题但是......这里有:
通用类型在C#编译时不会扩展。一种方法是编译器作为一种方法,具有唯一的令牌;你基本上调用方法令牌。从一开始,这就是.NET的基础之一:您可以编译每个方法的代码。这也意味着重载解析可能发生在C#编译时间,而不是JIT时间,这使得JIT更快。
在.NET中选择不支持通用特化是我认为主要是(运行时/ JIT /编译器)速度的选择。
如果我们要实现泛型重载,我们必须在C#编译时为每个方法标记存储多个方法实现,这将破坏上面提到的基础。然后,您可以使用相同的方法标记将每组方法编译到不同的(汇编程序)实现。在这一点上,你必须意识到这会给JIT带来很大的压力:JIT必须执行某种重载决策才能选择正确的#39;方法; C#编译器可以帮助JIT,因为它只会生成方法。仅这一点已经是不这样做的一个很好的理由 - 但让我们继续:
具有讽刺意味的是,模板扩展基本上是它在JIT中的工作方式。如果您有值类型,则将值类型放入项目&#39; T&#39;中,该方法实际上是&#39;扩展到IL和汇编程序的不同部分。换句话说:如果您有Foo<T>
,那么Foo<int>
将具有与Foo<double>
不同的IL和汇编程序代码。对于引用类型不是这种情况,因为可以对所有调用进行相同的vtable查找(接口约束)。换句话说,代码是reference types
共享的,而不是value types
。实际上你可以看到这种情况发生here。
通过这样做,您的JIT能够非常快速地生成非常好的代码。由于许多泛型是引用类型,它也从泛化规则中受益匪浅,这使得它快速流血。但是,如果您将专门化引用类型,它将破坏此优化 - 再次,这是一个不这样做的好理由。
因此,这给我们留下了关于值类型的通用专业化。此时,您必须意识到值类型可以由其他值类型组成,这意味着我们可以递归地组合类型。在C ++中,我们称之为type-lists
(google for:template meta programming),它们是扩展的编译时间。在C#中,这意味着(由于上述原因)类型列表在JIT阶段被扩展,并且可能内联,因为值类型不支持继承。这将给JIT编译器带来巨大压力,并且还会使实现起来更加复杂。
总而言之,是的,这是一个选择,但我相信如果添加此功能,它会基于JIT的性能和复杂性而得到很好的平衡。
所以我们现在知道为什么.NET中不支持通用特化。但是,另一种更简单的解决方案是可行的:就像async
和yield
不使用continuation一样,您也可以使用f.ex. Dictionary
或dynamic
执行实际的重载决策而不会破坏任何内容。这不会破坏.NET,它只会引入一些辅助方法/类。我猜您可以在MS Connect上将其作为功能请求提出。