How to implement some constrained .NET struct/structure (let's say LimitedString), where its state with default values (set by CLR to technical defaults, i.e. nulls, zeros, etc.) should be prohibited due to some design constraint?

For example in case of trivial struct LimitedString, properties are String Value and int MaxLength = 10, where value of the MaxLength property must be at least 1. Value 0 is not allowed by design. But when I initalize the structure, I have 0 there. How to force value 10 into defaults?

I see two options:

  1. Throw exception in static ( shared) parameterless constructor → force using only costructor(s) with parameters. Impractical, parameterless use is expected sometimes.
  2. Add helper private field IsInitialized and while it is false, assume default values, i.e. MaxLength = 10. Slightly higher complexity inside the struct.

Is option #2 a legitimate way or does this violate some design principles? Is there some better way than option #2?

EDIT: option #1 won't work anyway, mentioned constructor is called every time, even if other constructors are called.

  • Let 0 express 10 :) Then make property MaxLength { get { return 10 - maxLength} }; Also add similar transform in ctor. [whoops, I see there is already answer same like my comment] – apocalypse May 13 '16 at 15:53

Unless you have a really, really good reason that this has to remain a Struct, I would recommend that you convert it to a Class.
That way, initialisation is completely under your control, through the Constructor(s) of that Class.

If you really can't convert it, then I'd suggest creating a Factory Class to "construct" instances of this Struct; that class then takes on the responsibility for properly initialising instances of the Struct.

  • My actual case is very close to LimitedString I have shown. In fact, currently I have a class for it. Having a class for relatively simple value type often included in expressions does not look to me as a good idea, so I attempted to move it to struct. A factory class looks like a good idea (thanks) but programmers would need to be aware of it – maybe not very practical... also when considering its use in expressions. – miroxlav May 13 '16 at 11:53
  • Anyway, good answer. I'll re-evaluate supposed structure usage to decide whether to keep it as class or move to struct with that defaults trick shown in question as option #2 (code changes were minimal and it works nicely). Thank you for the insight. – miroxlav May 13 '16 at 12:21
  • 2
    Good answer. There have been many objects I have made over the years that fit most of the qualifications for a structure (small, primitive types, value semantics made sense in context), but I chose to change them to immutable classes and then overrode Equals simply because the 0's and null defaults were not a valid state that I wanted to exist. Overriding equals and keeping them immutable gets you close to the same usage as value semantics. – Mike May 13 '16 at 15:33
  • 1
    How does using factory help? It doesn't prevent the user from creating the default value. – svick May 13 '16 at 20:44

First note: I agree this should probably be a class. For a struct, though:

Is it possible to change the design? In LimitedString's case, it sounds like 0 is perfectly fine: a string with no characters. You can't enforce anything with a default constructor (like you suggested in #1) because structs (in C#) cannot contain explicit parameterless constructors.

For #2, maybe it's simpler to change the meaning of the struct's state to establish a good by-design default without adding an extra flag or a special-case 0 condition:

private int _maxLengthMinusTen; // By default 0, making MaxLength 10.
public int MaxLength => _maxLengthMinusTen + 10;

A constructor would perform the conversion from an input maxLength, or you could provide a private setter to keep the 10 localized in the code.

  • private int _maxLengthMinusTen; That's evil, I like it! – svick May 13 '16 at 20:43
  • good... actually MaxLength limit disallowing 0 is in databases... char/varchar/text... of course, 0 can stay for max as in varchar(max) – miroxlav May 13 '16 at 21:05
  • @miroxlav Ah, didn't think about it being dependent on a more set in stone limit. I loosened the language a bit there. – 31eee384 May 13 '16 at 21:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.