返回int应该返回泛型类的方法

时间:2015-06-05 07:03:08

标签: c# xml asp.net-mvc generics default-value

为了缩短它,我有一个看起来像这样的方法

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而不是我的通用类。

我现在没找到任何东西:/

3 个答案:

答案 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中不支持通用特化。但是,另一种更简单的解决方案是可行的:就像asyncyield不使用continuation一样,您也可以使用f.ex. Dictionarydynamic执行实际的重载决策而不会破坏任何内容。这不会破坏.NET,它只会引入一些辅助方法/类。我猜您可以在MS Connect上将其作为功能请求提出。