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;
但我的理念是私下使用字段,除非它们是const
或static
。
最后,我使用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()
正在创建无参数构造函数。