【问题标题】:C# - Calling a struct constructor that has all defaulted parametersC# - 调用具有所有默认参数的结构构造函数
【发布时间】:2012-11-03 09:57:55
【问题描述】:

我今天在创建 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 < 1 || value > 3)
{
    throw new ArgumentException("Value is out of range");
}

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

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

另外,我知道我可以使用这样的字段来代替我的 Value 属性:

public readonly int Value;

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

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

【问题讨论】:

  • 这不一定是使用struct 的全部理由,您仍然可以使用class 完成所有这些操作,然后抛出ArgumentNullException。 MSDN 概述了使用strcut 的主要原因:msdn.microsoft.com/en-us/library/ms229017.aspx
  • 有一天我应该在博客中介绍所有使用结构的疯狂示例!谢谢你!
  • 请注意,每次调用都会复制数据,因为它是一个结构......
  • 范围不是 1-3,而是范围 0-2,并且有一个属性可以将返回值修改为正确的范围;)
  • @Porges:你可以这样做,但这会很混乱,并且必须有详细的文档。我更喜欢可以为空的初始化字段,而不是在 proeprty 访问器上使用默认值。

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


【解决方案1】:

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

【讨论】:

  • "I don't think it's good design to have a struct ExampleStruct such that" [...]" 实际上,这不仅是糟糕的设计,甚至是不可能的;该语言不会让您做任何其他事情。
  • 为什么没有一个空的构造函数将其设置为 1?
  • @CarloV.Dango 尝试这样做并亲眼看看。你会得到一个编译器错误。
  • C# 使用结构就像编码 c++ 耶!
  • @Servy 我知道默认值始终是一切都为零的值。我是说考虑这个默认值“无效”是不好的。我的答案的扩展版本尝试转换该值,以便默认 1 在内部存储为零。有点像DateTime 结构,其中default(DateTime)Ticks 字段为零)表示 0001-01-01。
【解决方案2】:

其实MSDN对struct有一些很好的指导

考虑定义一个结构而不是一个类,如果 类型很小,通常寿命很短,或者通常嵌入 其他对象。

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

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

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

它是不可变的。

不必经常装箱。

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

请特别注意,他们不建议将 struct 用于大于 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&lt;int&gt;默认为null(即HasValue == false),因此如果为真,我们还没有设置,可以使用null-coalescing操作符返回我们默认使用1。如果我们确实在构造函数中设置了它,它将是非空的并取而代之的是那个值......

【讨论】:

  • 谢谢,詹姆斯。我最终将其更改为class。将ExampleClass 作为参数的方法默认为ExampleClass example = null。我放置文档说明如果设置为null,则默认为example ?? new ExampleClass()。由于它现在是一个类,它最终会调用 public ExampleClass(int value = 1) 的正确构造函数。
  • 关于? 和空合并运算符的好提示。这为我提供了Size.Empty 的解决方案,其中HeightWidth 的默认值可能为Double.NaN ...因此真正 Size
  • 关于与结构相关的性能,自上次编辑以来对语言进行了一些更新:devblogs.microsoft.com/premier-developer/…
【解决方案3】:

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

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

【讨论】:

  • 7.5.3.2 c# 规范中更好的函数成员。
  • 我只是说您应该将其包含在您的答案中。很明显正确,只是解释得不好。
  • 很公平。顺便说一句,类也是如此,只是您必须显式添加默认 ctor。
  • 该规范中的确切措辞:否则,如果MP 的所有参数都有相应的参数,而默认参数需要替换MQ 中的至少一个可选参数,那么@987654325 @ 优于 MQ
【解决方案4】:

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

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

【讨论】:

  • 您应该更清楚地了解: this() 与调用默认构造函数的关系。
  • 请注意,他的结构使用自动属性。他们引入了“特殊”的支持领域。因此,用户定义的构造函数必须链接this() 才能编译。
【解决方案5】:

尽管某些语言(如果没有其他语言,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,但这似乎相当不成熟。

【讨论】:

    猜你喜欢
    • 2017-09-27
    • 2018-04-08
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 1970-01-01
    • 2012-06-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多