今天我在创建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)
构造函数。ExampleStruct()
构造函数填充默认值。另外,我知道我可以使用这样的字段而不是我的Value
属性:
public readonly int Value;
但我的理念是私下使用字段,除非它们是const
或static
。
最后,我使用struct
而不是class
的原因是因为这只是一个保存不可变数据的对象,应该在构造时完全填充,以及作为参数传递,不应该是null
(因为它是作为struct
传递的值),这是结构的设计。
答案 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()
正在创建无参数构造函数。