C# - Calling a struct constructor that has all defaulted parameters

拜拜、爱过 提交于 2019-12-03 05:58:06
James Michael Hare

Actually, MSDN has some good guidance on struct

Consider defining a structure instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.

Do not define a structure unless the type has all of the following characteristics:

It logically represents a single value, similar to primitive types (integer, double, and so on).

It has an instance size smaller than 16 bytes.

It is immutable.

It will not have to be boxed frequently.

Notice that they are considerations for considering a struct, and its never a "this should always be a struct". That is because the choice to use a struct can have performance and usage implications (both positive and negative) and should be chosen carefully.

Notice in particular that they don't recommend struct for things > 16 bytes (then the cost of copying becomes more expensive than copying a reference).

Now, for your case, there is really no good way to do this other than to create a factory to generate a struct for you in a default state or to do some sort of trick in your property to fool it into initializing on first use.

Remember, a struct is supposed to work such that new X() == default(X), that is, a newly constructed struct will contain the default values for all fields of that struct. This is pretty evident, since C# will not let you define a parameterless constructor for a struct, though it is curious that they allow all arguments to be defaulted without a warning.

Thus, I'd actually suggest you stick with a class and make it immutable and just check for null on the methods that it gets passed to.

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;
    }
}

However, if you absolutely must have a struct for other reasons - but please consider the costs of struct such as copy-casts, etc - you could do this:

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;
    }
}

Notice that by default, the Nullable<int> will be null (that is, HasValue == false), thus if this is true, we didn't set it yet, and can use the null-coalescing operator to return our default of 1 instead. If we did set it in the constructor, it will be non-null and take that value instead...

I don't think it's good design to have a struct ExampleStruct such that

default(ExampleStruct)

i.e. the value where all instance fields are zero/false/null, is not a valid value of the struct. And as you know, when you say

new ExampleStruct()

that's exactly the same as default(ExampleStruct) and gives you the value of your struct where all fields (including "generated" fields from auto-properties) are zero.

Maybe you could do this:

public struct ExampleStruct
{
  readonly int valueMinusOne;

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

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

I guess the compiler is actually picking the automatic default ctor for structs here http://msdn.microsoft.com/en-us/library/aa288208(v=vs.71).aspx, rather than using your ctor with the default values.

added references: http://csharpindepth.com/Articles/General/Overloading.aspx (Optional parameters section)

Although some languages (CIL if nothing else) will allow one to define a struct constructor such that new T() has fields set to something other than their "all zeroes" defaults, C# and vb.net (and probably most other languages) take the view that since things like array elements and class fields must always be initialized to default(T), and since having them be initialized to something which didn't match new T() would be confusing, structures shouldn't define new T() to mean something other than default(T).

I would suggest that rather than trying to jinx a default parameter in a parameterized constructor, you simply define a static struct property which returns your desired default value, and replace every occurrence of new ExampleStruct() with ExampleStruct.NiceDefault;.

2015 Addendum

It appears that C# and VB.NET may ease the prohibition against defining parameterless struct constructors. This may cause a statement like Dim s = New StructType() to assign s a value which is different from the value given to new array items of type StructType. I'm not terribly keen on the change, given that new StructType is often used in places where something analogous to C#'s default(StructType) would be more appropriate if it existed. VB.NET would allow Dim s As StructType = Nothing, but that seems rather hokey.

why do you have public ExampleStruct(int value = 1) : this() ?

shouldn't it be public ExampleStruct(int value = 1)? I think the :this() is creating the no-parameter constructor.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!