I have classes like this
class MyClass{
const int Item1 = 1;
const int Item2 = 2;
const int Item3 = 3;
...
}
class MySecondClass : MyClass
{
const int Item1 = 4;
const int Item2 = 5;
const int Item3 = 6;
...
}
Is there an automatic way to set these values making sure they are sequential and still const? We have had problems on merges where two developers add an item and we end up with two items with the same value.
Currently we check if this happens with unit tests and fix it if a test breaks. But it would be better we could do something like this during compilation:
int i=0;
class MyClass{
const int Item1 = i;
const int Item2 = ++i;
const int Item3 = ++i;
...
}
class MySecondClass : MyClass{
const int Item4 = ++i;
const int Item5 = ++i;
const int Item6 = ++i;
...
}
Edit: This is legacy code in dozens of classes I can't change the structure to an enum.
class A
{
static A() { }
protected static int i = 0;
public static readonly int Item1 = i++;
public static readonly int Item2 = i++;
public static readonly int Item3 = i++;
}
class B : A
{
static B() { }
public static readonly int Item4 = i++;
public static readonly int Item5 = i++;
public static readonly int Item6 = i++;
}
You cannot use const because i++ is not considered a constant expression, but static readonly is a similar constraint.
According to 10.4.5.1 it is necessary to explicitly include the static constructors to inherit the initialisation order rules for static constructors.
According to 10.11:
If a class contains any static fields with initializers, those initializers are executed in textual order immediately prior to executing the static constructor.
And:
The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
- An instance of the class is created.
- Any of the static members of the class are referenced.
Because B references the static member i of A, the static constructor of A must execute first. Therefore you should be guaranteed (if I have interpreted this all correctly) that Item1 == 0, …, Item6 == 5 provided that there are no other descendants of A which also mutate i.
If you add another descendant C to A then you need to reference a static field of B from C if you wish to order B before C (or vice versa).
How about an enum?
enum Items : int
{
Item1 = 1,
Item2,
Item3,
// …
}
You can get your ints back out:
var x = (int)Items.Item1;
If you have a single class with all the constants, and the class contains nothing else, an enum is appropriate (see erisco's answer).
For more complex cases like the one you describe in your edited question, your best bet is probably code generation. You can make the classes partial, and generate part of the class from a T4 template, which allows you to implement any logic you like for the generated code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With