【问题标题】:Blindly converting structs to classes to hide the default constructor?盲目地将结构转换为类以隐藏默认构造函数?
【发布时间】:2010-11-28 14:33:52
【问题描述】:

我阅读了与该主题相关的所有问题,它们都给出了为什么 struct 上的默认构造函数在 C# 中不可用的原因,但我还没有找到任何人在遇到问题时提出一般的行动方案这种情况。

显而易见的解决方案是简单地将struct 转换为class 并处理后果。

是否有其他选项可以将其保留为struct

我在使用我们的一个内部商务 API 对象时遇到了这种情况。设计者将其从class 转换为struct,现在默认构造函数(之前是私有的)使对象处于无效状态。

我认为如果我们要将对象保留为struct,则应该引入一种检查状态有效性的机制(类似于IsValid 属性)。我遇到了很多阻力,并且对“任何使用 API 的人都不应该使用默认构造函数”的解释让我大吃一惊。 (注意:有问题的对象是通过静态工厂方法“正确”构造的,所有其他构造函数都是internal。)

在这种情况下,是否每个人都只是将structs 转换为classes 而不加考虑?

编辑:我希望看到一些关于如何将这种类型的对象保留为struct 的建议——上面讨论的对象更适合作为struct,而不是作为class

【问题讨论】:

  • 这是一个很好的问题。您可以设计一个结构,以使清零状态有效,但仅此一项并不总是构成可用的 API。如果存在默认构造函数,有些人将不可避免地调用它,因为它会在他们的 IDE 中弹出并提供阻力最小的路径。这将导致编译器应该捕获的问题,或者他们会浪费时间尝试在不可变的归零结构上设置属性。我希望 C# 允许您隐藏默认构造函数并强制人们调用 default(...) 如果他们真的想要一个归零的结构。
  • 其实研究这个比较多,我觉得可用性问题真的出在Intellisense而不是编译器上。似乎曾几何时,Visual Studio 没有在 Intellisense 中显示默认构造函数,在我看来,这是明智的做法。但是人们抱怨see here 并且在某些时候它一定已经改变了,因为现在 Visual Studio 2010 会弹出空构造函数作为第一个选项。

标签: c# .net constructor struct default-constructor


【解决方案1】:

对于struct,您需要设计类型,以便默认构造实例(字段全为零)是有效状态。您不 [不应] 在没有充分理由的情况下随意使用 struct 代替 class - 使用不可变引用类型没有任何问题。

我的建议:

  • 确保使用struct 的原因是有效的([真实] 分析器发现由于对非常轻量级的对象进行大量分配而导致严重的性能问题)。
  • 设计类型,使默认构造的实例有效。
  • 如果类型的设计是由本机/COM 互操作约束决定的,请包装功能并且不要将struct 暴露在包装器之外(私有嵌套类型)。这样您就可以轻松记录和验证约束类型要求的正确使用。

【讨论】:

  • 好答案。奇怪的是,对于我上面的特殊情况,你所说的都不适用,因为当对象是引用类型时,问题更容易引入代码中......因此为什么它被转换为结构。但在许多其他情况下,使用不可变引用类型将是一种可行的解决方案。
【解决方案2】:

这样做的原因是结构(System.ValueType 的一个实例)被 CLR 特殊处理:它被初始化为所有字段为 0(或默认值)。你甚至不需要创建一个 - 只需声明它。这就是需要默认构造函数的原因。

您可以通过两种方式解决此问题:

  1. 创建一个像 IsValid 这样的属性来指示它是否是一个有效的结构,就像你所指出的那样
  2. 在 .Net 2.0 中考虑使用 Nullable 来允许未初始化的(空)结构。

将结构更改为类可能会产生一些非常微妙的后果(就内存使用和对象标识而言,这在多线程环境中出现得更多),并且不那么微妙但难以调试未初始化对象的 NullReferenceExceptions。

【讨论】:

    【解决方案3】:

    无法定义默认构造函数的原因如下式所示:

    new MyStruct[1000];
    

    这里有 3 个选项

    1. 调用默认构造函数1000次,或者
    2. 创建损坏的数据(请注意,结构可以包含引用;如果您不初始化或清除引用,您可能会访问任意内存),或者
    3. 用零(在字节级别)清除分配的内存。

    .NET 对结构和类执行相同的操作:字段和数组元素用零清除。这也使结构和类之间的行为更加一致,并且没有不安全的代码。它还允许 .NET 框架不专门化 new byte[1000] 之类的内容。

    这是 .NET 要求并自行处理的结构的默认构造函数:将所有字节清零。

    现在,要处理这个问题,您有几个选择:

    • 将 Am-I-Initialized 属性添加到结构(如 HasValue 上的 Nullable)。
    • 允许归零的结构是有效值(如 0 是小数的有效值)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-06
      相关资源
      最近更新 更多