C# – 调用具有所有默认参数的结构构造函数

今天我在创建一个包含大量数据的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 。 原因是所有结构都具有公共参数构造函数。 所以,就像我在Main调用this()我的显式构造函数一样, new ExampleStruct()实际调用ExampleStruct()但不调用ExampleStruct(int value = 1)时会发生同样的事情。 因为它这样做,它使用int的默认值0作为Value

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

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

所以,就像现在一样,默认构造函数实际上创建了一个在我需要它的上下文中无效的对象。 任何人都知道如何:

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

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

 public readonly int Value; 

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

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

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

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

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

它逻辑上表示单个值,类似于基本类型(整数,双精度等)。

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

这是不可改变的。

它不必经常装箱。

请注意,它们是考虑struct考虑因素 ,它永远不会是“这应该始终是结构”。 这是因为使用struct的选择可能具有性能和使用含义(正面和负面),应谨慎选择。

特别要注意的是,他们不建议使用> 16字节的结构(然后复制成本比复制引用更昂贵)。

现在,对于你的情况,除了创建一个工厂来为你在默认状态下生成一个struct ,或者在你的属性中做一些trick来欺骗它在第一次使用时初始化时,除此之外没有其他好的方法。

请记住, struct应该起作用,使得new X() == default(X) ,即新构造的struct将包含该struct的所有字段的默认值。 这很明显,因为C# 不会让你为一个struct定义一个无参数构造struct ,尽管很奇怪它们允许所有参数默认没有警告。

因此,我实际上建议你坚持使用class并使其成为不可变的,只需检查传递给它的方法的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将为null (即HasValue == false ),因此如果这是真的,我们还没有设置它,并且可以使用null-coalescing运算符来返回我们的默认值1代替。 如果我们在构造函数中设置它,它将是非null并取代该值…

我不认为有一个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; } } 

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

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

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

我建议你不要试图在参数化构造函数中使用默认参数,而只需定义一个静态struct属性,该属性返回所需的默认值,并用new ExampleStruct()替换每个出现的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 ,但这看起来相当不错。

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

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