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

时间:2012-11-14 20:24:20

标签: c# struct optional-parameters default-constructor

今天我在创建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)的情况也是如此。由于它是这样做的,它使用int的默认值0作为Value

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

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

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

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

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

public readonly int Value;

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

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

5 个答案:

答案 0 :(得分:28)

实际上,MSDN对struct

有一些很好的指导
  

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

     

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

     

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

     

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

     

这是不可改变的。

     

不必频繁装箱。

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

特别注意他们不建议struct用于事物&gt; 16字节(然后复制的成本比复制参考更昂贵。)

现在,对于您的情况,除了创建工厂以在默认状态下为您生成struct或在某些trick中执行此类工作之外,实际上没有其他方法可以做到这一点。你的财产在第一次使用时会把它弄成初始化。

请注意,struct应该有效new X() == default(X),也就是说,新构建的struct将包含所有字段的默认值struct。这很明显,因为C#将让你为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<int>将为null(即HasValue == false),因此如果这是真的,我们尚未设置它,并且可以使用null-coalescing运算符返回我们的默认值1。如果我们 在构造函数中设置它,它将是非null并取代该值...

答案 1 :(得分:2)

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

default(ExampleStruct)

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

new ExampleStruct()

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

也许你可以这样做:

public struct ExampleStruct
{
  readonly int valueMinusOne;

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

  public ExampleStruct(int value)
  {
    valueMinusOne = value - 1;
  }
}

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

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

我建议您不要试图在参数化构造函数中使用默认参数,而只需定义一个静态struct属性,该属性返回所需的默认值,并将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,但这看起来很不错。

答案 4 :(得分:0)

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

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