17

struct我今天在创建一个保存一堆数据时遇到了这个问题。这是一个例子:

public struct ExampleStruct
{
    public int Value { get; private set; }

    public ExampleStruct(int value = 1)
        : this()
    {
        Value = value;
    }
}

看起来很好,花花公子。问题是当我尝试使用此构造函数而不指定值并希望对参数使用默认值 1 时:

private static void Main(string[] args)
{
    ExampleStruct example1 = new ExampleStruct();

    Console.WriteLine(example1.Value);
}

此代码输出0和不输出1。原因是所有结构都有公共的无参数构造函数。所以,就像我如何调用this()我的显式构造函数Main一样,同样的事情发生在new ExampleStruct()实际调用ExampleStruct()但不调用的地方ExampleStruct(int value = 1)。因为它这样做了,所以它使用int0 的默认值作为Value.

更糟糕的是,我的实际代码正在检查该int value = 1参数是否在构造函数的有效范围内。将其添加到ExampleStruct(int value = 1)上面的构造函数中:

if(value < 1 || value > 3)
{
    throw new ArgumentException("Value is out of range");
}

因此,就目前而言,默认构造函数实际上创建了一个在我需要它的上下文中无效的对象。任何人都知道我可以:

  • A. 调用ExampleStruct(int value = 1)构造函数。
  • B. 修改为ExampleStruct()构造函数填充默认值的方式。
  • C. 其他一些建议/选项。

另外,我知道我可以使用这样的字段而不是我的Value属性:

public readonly int Value;

但我的理念是私下使用字段,除非它们是constor static

最后,我使用 astruct而不是 aclass的原因是因为这只是一个保存非可变数据的对象,在构造它时应该完全填充,当作为参数传递时,不应该null(因为它通过值作为 a struct) 传递,这就是 struct 的设计目的。

4

5 回答 5

29

实际上,MSDN 有一些很好的指导struct

如果类型的实例很小且通常短暂存在或通常嵌入在其他对象中,请考虑定义结构而不是类。

除非类型具有以下所有特征,否则不要定义结构:

它在逻辑上表示单个值,类似于原始类型(整数、双精度等)。

它的实例大小小于 16 字节。

它是不可变的。

它不必经常装箱。

请注意,它们是考虑a的考虑因素struct,它绝不是“这应该始终是一个结构”。这是因为选择使用 astruct可能会对性能和使用产生影响(正面和负面),因此应谨慎选择。

请特别注意,他们不推荐struct大于 16 字节的内容(这样复制的成本就会比复制引用的成本更高)。

struct现在,对于您的情况,除了创建一个工厂以在默认状态下为您生成一个或在您的属性中执行某种操作trick以使其在首次使用时进行初始化之外,实际上没有什么好的方法可以做到这一点。

请记住, astruct应该这样工作new X()== default(X),也就是说,新构造的 astruct将包含 that 的所有字段的默认值struct。这很明显,因为 C#不允许您为 a 定义无参数构造函数struct,尽管奇怪的是它们允许默认所有参数而不发出警告。

因此,我实际上建议您坚持使用 aclass并使其不可变,然后检查null它传递给的方法。

public class ExampleClass
{
    // have property expose the "default" if not yet set
    public int Value { get; private set; }

    // remove default, doesn't work
    public ExampleStruct(int value)
    {
        Value = value;
    }
}

但是,如果您出于其他原因绝对必须拥有- 但请考虑复制演员等struct的成本- 您可以这样做:struct

public struct ExampleStruct
{
    private int? _value;

    // have property expose the "default" if not yet set
    public int Value
    {
        get { return _value ?? 1; }
    }

    // remove default, doesn't work
    public ExampleStruct(int value)
        : this()
    {
        _value = value;
    }
}

请注意,默认情况下,Nullable<int>将是null(即HasValue == false),因此如果这是真的,我们还没有设置它,并且可以使用空合并运算符来返回我们的默认值1。如果我们确实在构造函数中设置了它,它将是非 null 并取而代之的是那个值......

于 2012-11-14T20:33:27.747 回答
2

我不认为有这样的设计是struct ExampleStruct好的

default(ExampleStruct)

即所有实例字段为零/假/空的值不是结构的有效值。如你所知,当你说

new ExampleStruct()

default(ExampleStruct)这与并为您提供所有字段(包括来自自动属性的“生成”字段)为零的结构的值完全相同。

也许你可以这样做:

public struct ExampleStruct
{
  readonly int valueMinusOne;

  public int Value { get { return valueMinusOne + 1; } }

  public ExampleStruct(int value)
  {
    valueMinusOne = value - 1;
  }
}
于 2012-11-14T20:32:52.923 回答
1

我猜编译器实际上是在这里http://msdn.microsoft.com/en-us/library/aa288208(v=vs.71).aspx为结构选择自动默认 ctor ,而不是使用带有默认值的 ctor .

添加参考: http ://cshapindepth.com/Articles/General/Overloading.aspx (可选参数部分)

于 2012-11-14T20:35:44.197 回答
1

尽管某些语言(如果没有其他语言,CIL)将允许定义一个结构构造函数,以便new T()将字段设置为“全零”默认值以外的其他内容,但 C# 和 vb.net(可能还有大多数其他语言)认为,因为数组元素和类字段之类的东西必须始终初始化为default(T),并且由于将它们初始化为不匹配的东西new T()会令人困惑,因此结构不应定义new T()default(T).

我建议不要尝试在参数化构造函数中使用默认参数,而只需定义一个返回所需默认值的静态结构属性,并将每次出现的地方替换new ExampleStruct()ExampleStruct.NiceDefault;.

2015 年附录

似乎 C# 和 VB.NET 可以放宽对定义无参数结构构造函数的禁令。这可能会导致语句喜欢Dim s = New StructType()分配s一个不同于赋予类型新数组项的值的值StructType。我并不是非常热衷于这种变化,因为它new StructType经常用于类似于 C# 的东西default(StructType)如果存在的话会更合适的地方。VB.NET 将允许Dim s As StructType = Nothing,但这似乎相当做作。

于 2012-11-14T22:08:39.343 回答
0

你为什么有public ExampleStruct(int value = 1) : this()

不应该public ExampleStruct(int value = 1)吗?我认为这:this()是创建无参数构造函数。

于 2012-11-14T20:40:19.037 回答