【问题标题】:How to prevent a struct's default constructor to be called?如何防止调用结构的默认构造函数?
【发布时间】:2016-08-04 22:06:59
【问题描述】:

有什么办法可以防止结构体的默认构造函数被调用?

我的项目中有几个结构,其中一些我不能让默认构造函数在任何情况下被调用(这将导致我的代码中出现很多不需要的行为)。

PS.:在编码某些特殊结构(无法使用默认构造函数实例化)被“错误地”实例化时,任何仅指示我的解决方案对我来说都很好。对于我的情况,一个简单的编译器警告就足够了!

【问题讨论】:

  • 将构造函数设为私有就足够了?!
  • C# 中的默认结构构造函数不是 CLR 级别的构造函数。它们是严格的零初始化构造,看起来(在语法上)像构造函数。它们会导致什么样的不良行为?你能告诉我们更多关于你的用例吗?
  • 我会将我的结构更改为类人。我正在使用结构,因为我试图设置 blittable 自定义类型,但在我的特定场景中这似乎是不可能的。

标签: c# struct default-constructor


【解决方案1】:

你不能。

天生

  • struct 的所有字段都必须初始化为某个值。
  • struct 不能有无参数构造函数
  • 无参数构造函数将被隐式调用,无法绕过。

如果您需要此类行为,请使用class 或查看您的设计。

【讨论】:

  • 我将更改一个类的类型。将类的默认构造函数设为私有我确保在任何情况下都不会调用它?谢谢!
  • @andresantacruz 即使您将结构的构造函数设为私有,仍然会从零字节内存范围创建值 - 考虑一下会发生什么:struct Foo { public Int32 X; } void() { Foo[] structArray = new Foo[100]; structArray[50].X = 123; }
【解决方案2】:

正如 Aybe 指定的那样,您不能这样做,但您希望可以在结构中添加一个字段,例如 IsInitialized,默认为 false,并使用它来验证值并在为 false 时抛出。

【讨论】:

  • 不错的约会。将类的默认构造函数设为私有我确保在任何情况下都不会调用它?谢谢!
【解决方案3】:

当您处理结构时,C# 想要确保您已初始化结构的所有字段。

一种选择是提供一个构造函数,它首先调用默认构造函数,然后再进行自定义初始化:

 public S(int x) : this()
 {
     /* custom initialization logic */
 }

另一种选择是提供一个构造函数,该构造函数可证明在自己的主体中初始化结构的所有字段,在这种情况下,您不需要链接默认构造函数:

 public S(int x)
 {
     this.X = x;
     /* custom initialization logic */
 }

另一种选择是跳过调用构造函数,而是在实际使用之前手动初始化结构:

S s;
s.X = 5;

在上述选项中,只有第一个执行对默认构造函数的逻辑调用,而其他两个跳过调用。

坏消息是,在幕后,出于安全原因,CLR 将始终为您提供零初始化内存,这几乎完全等同于调用 C# 默认构造函数。你可以这样观察:

S s;
S* ptr = &s;
int x = (*ptr).X;
Console.WriteLine(x); // will print 0

好消息是,在大多数情况下,这无关紧要。 C# 默认构造函数并不是 CLR 意义上的真正构造函数。任何T : struct 的表达式new T() 等价于default(T),这只是表示获取T 类型结构的零初始化实例的一种方式。这也是为什么您不能将其设为私有或向其添加自定义逻辑的原因。

因此,C# 默认结构构造函数不能有任何逻辑外部可观察的副作用。这给我们带来了一个问题:您要避免什么样的影响?

请记住,如果您的问题是您的结构对于全零位模式无效,并且您担心某些客户端代码可能会尝试这样使用它,那么有一种解决方法:

struct S
{
    private bool _isValid;
    /* rest of your struct */
}

任何零初始化的结构都不会设置其有效性标志,只要在结构上执行任何操作(您可以控制),您就可以使用它来引发异常。

毕竟,如果出于任何原因您仍然需要一个结构实例,该实例以某种方式跳过了调用任何构造函数(无论是真实的还是表面上的),您必须自己为该结构分配一些内存:

S* ptr = (S*) Marshal.AllocHGlobal(Marshal.SizeOf<S>());

您现在可以使用 (*ptr).Xptr-&gt;X 形式的表达式直接读取和写入该结构。

【讨论】:

  • 我需要禁止调用默认构造函数(以及任何其他可能的默认实例化方法,例如您所说的 default(T)),因为对于这些特定的自定义类型,我需要运行一些启动代码在内存中创建对象之后 - 在任何其他代码获取引用之前。所以,我从你所说的理解是,我想要完成的事情是不可能的,因为除了默认构造函数之外,还有其他方法可以创建实例;那正确吗?非常感谢!
  • @ptr0x 在 C# 中,如果结构存在,那么任何人都可以执行零初始化。即使您隐藏了默认构造函数,仍然有人可以执行new S[1] 并获得一个零初始化的结构实例。我想不出任何简单的方法来解决这个问题。也许如果您的公共结构实际上只是真正结构的句柄,而真正的结构保持在内部?我只是猜测。
  • 好的,如果我将这些类型从结构体更改为类,将默认构造函数设为私有就足以禁止任何默认初始化方式?
  • @ptr0x 绝对。 default(T) 是所有类的null,并且不需要构造函数。但是如果你想维护 blittable 类型,你仍然可以使用隐藏的有效性标志。
  • 知道了。我不能再维护这些类型的 blittable 了。努力保持这种状态,但我找不到可行的方法来做到这一点。我正在用 C# 开发一个 mmorpg 服务器,我需要对数据包数据进行一些高级表示。我最初的想法是制作这些数据包的 blittable 结构并只进行指针转换(从 byte[] 到 struct*),但在我的情况下无法做到。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多