I am trying to use a struct to store a musical note. The note is represented by a singular int, but it has lots of other fields that give other representations (note name, octave, ect.).
The problem is that a struct can be created without the constructor being called, meaning those fields are given the default values. These values are invalid for the note the struct is supposed to represent.
There are four options I can think of to go about this. (for these examples I have simplified what I'm trying to do):
The first way uses a parameterless constructor. This works, but the struct can still be created under some circumstances without the constructor being called.
readonly struct Number
{
public readonly int Value;
public readonly int ValueTimesTen;
public Number()
{
this = new(1);
}
public Number(int value)
{
if (value is > 0 and <= 10)
{
Value = value;
ValueTimesTen = Value * 10;
}
else
{
throw new ArgumentException("Number has the be between 1 and 10.");
}
}
}
The second way is to use properties the ensure the struct always give valid outputs. In this example some properties are recalculated everytime they are used. This is more costly in the real thing that I'm really trying to do.
readonly struct Number
{
private readonly int _value;
public int Value
{
get
{
if (GetValid(_value))
{
return _value;
}
else
{
return 1;
}
}
}
public int ValueTimesTen => Value * 10;
private static bool GetValid(int number)
{
return (number is > 0 and <= 10);
}
public Number(int value)
{
if (GetValid(value))
{
_value = value;
}
else
{
throw new ArgumentException("Number has the be between 1 and 10.");
}
}
}
The third way is to simply use a class. This doesn't feel right for something thats immutable and is really just a single number. But it might be the best option.
The fourth way is to simply allow the struct to have an invalid state, and to make sure I never allow this to happen elsewhere in my code.
Whats the best way to go about this?
CodePudding user response:
Basically, you shouldn't create a parameterless constructor for struct in first way
Check this on Microsoft documentation: Struct C#
The default value of a struct corresponds to the value returned by the default constructor of the struct (§8.3.3). Unlike a class, a struct is not permitted to declare a parameterless instance constructor. Instead, every struct implicitly has a parameterless instance constructor, which always returns the value that results from setting all fields to their default values.
Note: Structs should be designed to consider the default initialization state a valid state. In the example
using System;
struct KeyValuePair
{
string key;
string value;
public KeyValuePair(string key, string value)
{
if (key == null || value == null)
{
throw new ArgumentException();
}
this. Key = key;
this. Value = value;
}
}
the user-defined instance constructor protects against null values only where it is explicitly called. In cases where a KeyValuePair variable is subject to default value initialization, the key and value fields will be null, and the struct should be prepared to handle this state.