【问题标题】:Return int in method supposed to return generic class在应该返回泛型类的方法中返回 int
【发布时间】:2015-06-05 16:57:00
【问题描述】:

简而言之,我有一个看起来像这样的方法

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 而不是我的泛型类。

我暂时没有找到任何东西:/

【问题讨论】:

  • 不,有充分的理由,您发现它是默认设置,您希望如何发现一个错误,因为您团队中的某个开发人员更改了它?

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


【解决方案1】:

尝试做类似的事情:

return (T)(object)-1;

【讨论】:

  • 正在编译!我要进行一些测试;) Ty!
  • 是的,我通常也是这样做的。也就是说,我不太喜欢它,因为它意味着一个盒子/拆箱。如果有更好的东西就好了 - 也就是说,我还没有找到更好的解决方案。
  • @atlaste 如果你有一个固定值,你可以将它缓存在一个通用结构中......否则你可以使用Expression 树并自动生成一些代码。请参阅stackoverflow.com/a/18182340/613130中的第一个示例
  • @xanatos 我不喜欢它的原因不是因为性能,而是因为类型安全。我想要的基本上是 C++ 模板专业化,但我知道那是不可能的。在大多数情况下,您可以使用dynamic 和重载解析规则,这是我认为的下一个最佳选择——但这也破坏了类型安全。
  • @atlaste 在编译时没有办法做到这一点...从技术上讲,您可以使用Fody 执行某些操作,以在“编译后”生成错误,或者通过编写自定义规则代码分析。
【解决方案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);
}

【讨论】:

  • 您的问题的解决方案将完全基于意见。 不,该解决方案不基于意见。选择解决方案是基于意见的。可能的解决方案是基于语言的。他没有问“最好的方法是什么”。他问“我该怎么做”
【解决方案3】:

正如我在 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&lt;T&gt;,那么Foo&lt;int&gt; 将具有与Foo&lt;double&gt; 不同的IL 和汇编代码。这不是引用类型的情况,因为可以对所有调用进行相同的 vtable 查找(接口约束)。换句话说,代码是为reference types 共享的,而不是为value types 共享的。您实际上可以看到这种情况发生在here

通过这样做,您的 JIT 能够非常快速地生成非常好的代码。由于很多泛型是引用类型,它也从泛化规则中受益匪浅,这使得它流血得很快。但是,如果您将引用类型专门化,它会破坏这种优化 - 同样,这是一个不这样做的好理由。

因此,这使我们仅对值类型进行了泛型专业化。此时,你必须意识到值类型可以由其他值类型组成,这意味着我们可以递归地组合类型。在 C++ 中,我们将这些东西称为 type-lists(google for:模板元编程),它们是扩展的编译时。在 C# 中,这意味着(由于上述原因)类型列表在 JIT 阶段被扩展,并且可能发生内联,因为值类型不支持继承。这会给 JIT 编译器带来巨大的压力,而且实现起来也更加复杂。

总而言之,是的,这是一个选择,但我相信如果添加此功能,基于 JIT 的性能和复杂性,这是一个非常平衡的选择。

所以我们现在知道为什么 .NET 不支持泛型特化。然而,另一种更简单的解决方案是可能的:就像 asyncyield 不要使用延续,你也可以使用 f.ex。 Dictionarydynamic 在不破坏任何东西的情况下执行实际的重载解决方案。这不会破坏 .NET,它只会引入一些辅助方法/类。我想您当然可以在MS Connect 上将其作为功能请求提出。

【讨论】:

  • 非常好的理由 这些好的理由应该是什么? “泛型专业化”(在 C++ 中作为(部分)模板专业化存在)的缺失特性只是微软在 .NET 2.0 中所做的选择。
  • @xanatos 我已尽力回答,希望我没有犯大错。
  • 这是我会写的:-)(但我的英语会差很多)。最后他们选择优化泛型的参考案例,这使得泛型特化、泛型方法中使用的方法的后期绑定等变得更加困难。他们肯定为此付出了很多努力(因为这是一个重大决定,而且两个选择都很好)。我没有他们肯定拥有的数据,所以我不知道这是否是最佳选择。我只能相信他们 :-) 当然,它是最容易使用反射方式的......拥有通用专业化会使反射变得非常复杂
猜你喜欢
  • 2016-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多