【问题标题】:Should I avoid nested types in generic types?我应该避免泛型类型中的嵌套类型吗?
【发布时间】:2016-03-12 09:42:18
【问题描述】:

如果我实现了一个泛型类型,它可能会被大量类型参数实例化,我是否应该避免(出于 JIT 性能/代码大小等原因)有许多嵌套的非泛型类型?

例子:

public class MyGenericType<TKey, TValue>
{
    private struct IndexThing
    {
        int row; int col;
    }

    private struct SomeOtherHelper
    {
        ..
    }

    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { }

}

同样有效的替代方法是将非泛型类型放在外部,但它们会污染命名空间。 有最佳做法吗?

public class MyGenericType<TKey, TValue>
{
    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { }
}

internal struct IndexThingForMyGenericType
{
   int row; int col;
}

internal struct SomeOtherHelper
{
   ...
}

【问题讨论】:

  • 一如既往地优化而不遇到瓶颈。只需以最有意义的方式进行编码,大多数情况下它不会成为问题。关于这个问题,我不知道,但你可以在 BCL 中看到很多嵌套的私有类,所以从设计的角度来看,我不介意走这条路。你说的第二个可能会令人困惑
  • 这段代码在语义上不一样。在您的第一个示例中,SomeOtherHelper 不是单一类型,因为它取决于泛型参数。例如,typeof(MyGenericType&lt;int, string&gt;.SomeOtherHelper)typeof(MyGenericType&lt;long, bool&gt;.SomeOtherHelper) 不同。
  • 如果您的问题是因为您希望命名空间中的类更干净,则将其添加到子命名空间,例如,如果您的命名空间是 MyNamespace,然后添加到 MyNamespace.AccesoryNamespace。
  • @Enigmativity 我意识到它们变成了不同的类型,但我不需要它们是不同的(除非它们有静态字段或者我使用例如 typeof(..) 在什么它们有明显不同的方式吗?)。我想嵌套它们,因为它们在我的类型中使用。但我不想支付性能费用。
  • @Gusman 我想我在想的是,在我班以外的任何人都不能使用的任何地方都有一种可发现的类型是一种气味。这是一种微弱的气味,所以如果对性能有一点好处,我很乐意为此付出代价。

标签: c# .net generics


【解决方案1】:

在 C# 中,泛型类型的每个嵌套类型本质上都是泛型的。编译器也会使嵌套类型成为泛型(在我们不知情的情况下)。更多信息请参考this article

尽管泛型共享引用类型的 JIT 代码,如 this interview 中所述,但与非泛型类相比,它有一些开销。每个值类型都有自己的 JIT 代码。

  • 如果该类型仅在泛型类中使用--私有嵌套类型更有意义。

  • 如果该类型在其他地方使用,那么理想情况下它应该是非嵌套类型(作为内部)。

也就是说,如果您的嵌套类型在这种情况下没有使用类型参数T,则它不需要是泛型类型的嵌套类型,因此它也成为泛型类型。

大多数情况下这无关紧要,但如果您担心在运行时创建的许多类型,您可以重构您的泛型类型以拥有一个非泛型基类,该基类充当嵌套类型的容器类型并公开嵌套类型受保护。

public class NonGenericBase
{
    protected struct IndexThing
    {
        int row; int col;
    }

    protected struct SomeOtherHelper
    {
        ..
    }
}

public class MyGenericType<TKey, TValue> : NonGenericBase
{
    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { }

}

这样您就可以共享相同的嵌套类型。没有运行时开销。每个类型参数没有单独的类型。现在typeof(MyGenericType&lt;int, string&gt;.SomeOtherHelper) 将等于typeof(MyGenericType&lt;long, bool&gt;.SomeOtherHelper)

【讨论】:

  • 我认为非泛型基类可能是要走的路。唯一不好的是,如果它实际上没有用,它仍然必须(至少)作为我的泛型类型是公共的,所以现在我污染了公共 api 而不仅仅是内部的 :( 我仍然认为这是最少的虽然不好的解决方案。谢谢
【解决方案2】:

虽然这并不能完全回答问题,但请注意,对于引用类型,代码是共享的,因为在内部它都是关于指针的。因此,您不必担心代码库臃肿。引用this 答案(Anders Hejlsberg 是被引用者)。

现在,我们接下来要做的是对所有类型的实例化值 类型——例如List&lt;int&gt;List&lt;long&gt;List&lt;double&gt;List&lt;float&gt;—— 我们创建可执行本机代码的唯一副本。所以List&lt;int&gt; 得到 自己的代码。 List&lt;long&gt; 有自己的代码。 List&lt;float&gt; 拥有自己的 代码。 对于所有引用类型,我们共享代码,因为它们是 代表性相同。这只是指针。

【讨论】:

  • 是的,但是对于相同大小的值类型,它甚至不共享,所以MyGenericType&lt;DateTime, DateTime&gt;.IndexThing afaik。不会重复使用 MyGenericType&lt;DateTime, long&gt;.IndexThing 等。使用 2+ 类型参数,即使所有引用实例共享代码,我也担心组合爆炸!
  • @AndersForsgren 是的,我知道。这就是为什么我说它是为 reference 类型共享的。
猜你喜欢
  • 1970-01-01
  • 2016-09-14
  • 1970-01-01
  • 1970-01-01
  • 2011-05-08
  • 2022-08-05
  • 2019-03-10
  • 1970-01-01
  • 2020-06-29
相关资源
最近更新 更多