10

Is there any way to prevent the default constructor of a struct to be called?

I have several structs in my project and some of them I can not let the default constructor to be called at any circumstances (that will be cause a lot of unwanted behaviour in my code).

PS.: Any solution that just indicates me when coding that some of that special structs (that can not be instantiated with the default constructor) are being instantiate "wrongly" is fine for me. A simple compiler warning should be enough for my case!

4
  • Making the constructor private should suffice?! Commented Apr 13, 2016 at 16:47
  • 1
    Default struct constructors in C# aren't constructors at the CLR level. They are strictly zero initialization constructs made to look (syntactically) like constructors. What kind of unwanted behavior do they cause? Can you tell us more about your use case? Commented Apr 13, 2016 at 16:48
  • I will change my structs to classes guys. I was using structs because I was trying to set blittable custom types, but in my specific scenarious it seems to be impossible. Commented Apr 13, 2016 at 17:17
  • The short answer is that it isn't possible, you have to work around it in other ways. If the problem here is related to value validation I recently suggested a workaround here: stackoverflow.com/a/78862749/533837 ...not sure it helps you though? Commented Aug 12, 2024 at 17:09

6 Answers 6

10

You cannot.

By nature,

  • all fields of a struct must be initialized to some value.
  • a struct cannot have a parameter-less constructor
  • the parameter-less constructor will be implicitly called, there's no way around it.

Either use a class if you need such behavior or review your design.

Sign up to request clarification or add additional context in comments.

2 Comments

I will change the type for a class. Making a class's default constructor private I ensure it will not be called at ANY circumstances? Thanks!
@andresantacruz Even if you make a struct's constructor private, values will still be created from a range of zero-byte memory - consider what happens with this: struct Foo { public Int32 X; } void() { Foo[] structArray = new Foo[100]; structArray[50].X = 123; }.
6

When you're dealing with structs, C# wants to make sure that you've initialized all of the struct's fields.

One option is to provide a constructor that will call the default constructor first, then do your custom initialization later:

 public S(int x) : this()
 {
     /* custom initialization logic */
 }

Another option is to provide a constructor that provably initializes all fields of the struct in its own body, in which case you don't need to chain the default constructor:

 public S(int x)
 {
     this.X = x;
     /* custom initialization logic */
 }

Another option is to skip calling a constructor, and instead initialize the struct manually, just before you actually use it:

S s;
s.X = 5;

Out of the above options, only the first performs a logical call to the default constructor, while the other two skip the call.

The bad news is that, behind the scenes, the CLR will always give you zero-initialized memory for safety reasons, which is pretty much exactly equivalent to calling the C# default constructor. You can observe it like this:

S s;
S* ptr = &s;
int x = (*ptr).X;
Console.WriteLine(x); // will print 0

The good news is that, for most cases, this doesn't matter. The C# default constructors aren't really constructors in the CLR sense. The expression new T() for any T : struct is equivalent to default(T), which is simply a way to express getting a zero-initialized instance of a struct of type T. It's also why you can't make it private or add custom logic to it.

Therefore, C# default struct constructors cannot have any logical externally observable side-effects. Which brings us to a question: what kinds of effects are you trying to avoid?

Keep in mind that if your problem is that your struct is invalid for the all-zero bit pattern and you're afraid that some client code might attempt to use it like that, there is a workaround:

struct S
{
    private bool _isValid;
    /* rest of your struct */
}

Any zero-initialized struct will have its validity flag not set, and you can use that to throw exceptions as soon as any operation (that you can control) is performed on your struct.

After all this, if for any reason you still need to a struct instance that has somehow skipped calling any constructor (whether a real or an apparent one), you'll have to allocate some memory for the struct yourself:

S* ptr = (S*) Marshal.AllocHGlobal(Marshal.SizeOf<S>());

You can now use expressions of the form (*ptr).X or ptr->X to read and write to that struct directly.

6 Comments

I need to forbid the default constructor (along with any other possible default instantiation method, like the default(T) you said) to be called because for these specific custom types I need to run some initiation code right after the object is created in memory - before any other code gets the reference. So, what I understood from what you said is that what I want to accomplish is impossible because there are other ways to create an instance besides the default constructor; is that right? Thank you very much!
@ptr0x In C#, if a struct exists, then zero-initialization can be performed by anyone. Even if you hid the default constructor, someone could still do new S[1] and get one zero-initialized instance of your struct. I can't think of any easy way out of this. Maybe if your public struct is effectively just a handle to the real struct, and the real struct is kept internal? I'm just speculating.
Okay, and if I change these types from struct to classes, making the default constructor private is enough to forbid any way of default initialization?
@ptr0x Definitely. default(T) is null for all classes and no constructor is necessary. But if you want to maintain blittable types, you can still go for the hidden validity flag.
Got it. I can't maintain these types blittable anymore. Tried so hard to keep that way, but I just can't find a viable way to do this. I'm developing a mmorpg server in C# and I need to make some high-level representation of the packet data. My initial thoughts were to make blittable structs of these packets and just make a pointer cast (to struct* from byte[]), but this can not be done in my case.
|
3

Starting with C# 10 you can do this:

public struct MyStruct
{
    [Obsolete("You may not use the parameterless constructor.", error: true)]
    public MyStruct()
    {
        throw new InvalidOperationException("You may not use the parameterless constructor.");
    }
}

This doesn't change the fact that structs can still be initialized without using any constructor. For example:

MyStruct uninitialized = default;

var allUninitialized = new MyStruct[10];

For more info, see Structure types (C# reference)

Comments

2

As Aybe specified you cannot do it but it you want you can add a field in the struct like IsInitialized that will default to false and make the code using it to verify the value and throw if false.

1 Comment

Nice apointment. Making a class's default constructor private I ensure it will not be called at ANY circumstances? Thanks!
2

I bash against the same issue myself. For now (rather for ever) I found this workaround: define public parameterless constructor and mark it as obsolete:

[Obsolete("Must not be used without parameters", true)]

This will prevent the constructor from being called explicitly. Obsolete is not a suitable word, of course, and it will still appear among the hints on typing but it kinda does the trick

2 Comments

Only available for C# 10+ though (to declare a parameterless constructor) but I like it, close as we can get obviously.
Keep in mind that this doesn't work against the use of the default keyword.
0

As @aybe responded, this isn't possible. However, I found a workaround for my scenario: We can decorate property-getter with a guard that throws exception if an invalid value is stored and accessed.

So this do not prevent storing invalid value but stopping working with invalid value.

This also means that guard check is executed every time you access the property.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.