如何防止结构体的默认构造函数被调用?

问题描述 投票:0回答:5

有什么方法可以阻止调用结构体的默认构造函数吗?

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

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

c# struct default-constructor
5个回答
10
投票

你不能。

本质上,

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

如果您需要此类行为,请使用

class
或检查您的设计。


6
投票

当您处理结构时,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 意义上的真正构造函数。任何

new T()
的表达式
T : struct
等价于
default(T)
,这只是表达获取
T
类型结构体的零初始化实例的一种方式。这也是为什么您不能将其设为私有或向其添加自定义逻辑的原因。

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

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

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

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

在这一切之后,如果出于某种原因您仍然需要一个以某种方式跳过调用任何构造函数(无论是真实的还是明显的构造函数)的结构实例,您将必须自己为该结构分配一些内存:

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

您现在可以使用

(*ptr).X
ptr->X
形式的表达式直接读取和写入该结构。


2
投票

正如 Aybe 所指定的,你不能这样做,但如果你想要的话,你可以在结构中添加一个字段,如

IsInitialized
,它将默认为 false,并使代码使用它来验证值并在 false 时抛出。


0
投票

正如@aybe 回应的那样,这是不可能的。但是,我找到了适合我的场景的解决方法:我们可以用一个守卫来装饰 property-getter,如果存储和访问无效值,该守卫会抛出异常。

因此,这不会阻止存储无效值,但会停止使用无效值。

这也意味着您每次访问该房产时都会执行警卫检查。


0
投票

我自己也反对同样的问题。现在(而不是永远)我找到了这个解决方法:定义公共无参数构造函数并将其标记为过时:

[Obsolete("Must not be used without parameters", true)] [EditorBrowsable(EditorBrowsableState.Never)] public CodeFile(){ }
这将防止显式调用构造函数。当然,“过时”不是一个合适的词,它仍然会出现在打字提示中,但它确实起到了作用

© www.soinside.com 2019 - 2024. All rights reserved.