Question

I have a c# struct where I need to forbid calling the no args constructor on it.

MyStruct a;
/// init a by members   // OK
MyStruct b = MyStruct.Fact(args); // OK, inits by memebers

MyStruct s = new MyStruct(); // can't have that

I'm doing this mostly to force explicet values for all members as there are no valid default values and all members must have valid values.

In C++ this would be easy, add a private constructor but c# doesn't allow that.

Is there a way to prevent the above?

I really need to enforce using a factory so preventing all public constructor calls would work just as well.


Full discloser: to avoid a mono dependency, the c# app is being automatically translated to D where new Struct() results in a pointer and this is mucking things up for me. However this question is relevant despite that so just ignore it.

Was it helpful?

Solution

You can't. All structs have a public parameterless constructor by definition in C#. (In the CLR almost none of them do, but they can always act as if they have.) See this question for why you can't define your own parameterless constructors on structs (in C#, anyway).

In fact, you can prevent this statement if you're happy to write your value type in IL. I've just checked, and if you make sure that your value type only has a parameterless constructor, and make it internal, you won't be able to write MyStruct ms = new MyStruct(); But this doesn't prevent:

MyStruct[] array = new MyStruct[1];
MyStruct ms = array[0];

which works around the new restriction - so it doesn't really buy you anything. That's quite good really, as messing around in IL would be messy.

Are you sure you really want to be writing a struct in the first place? That's almost never a good idea.

OTHER TIPS

You can't.

All values in a struct must be initialized at construction time, and there's no way to do that outside the constructor.

What exactly are you trying to accomplish by doing that? Structs are value types, so you'll get a "new" struct for most operations. It will be very difficult to enforce the sorts of constraints you'd use a factory for on a struct.

Anyone can create a struct at any time without calling a constructor, as long as they have access to the struct. Think about it this way:

If you create an array of objects with 1000 elements, they all get initialized to null, so no constructors are called.

With structs, there is no such thing as null. If you create an array with 1000 DateTime objects, they are all initialized to zero, which equals DateTime.Min. The designers of the runtime chose to make it so you could create an array of structs without calling the constructor N times--a performance hit many people wouldn't realize was there.

Your factory idea is a good one, though. Would it meet your needs to create an interface and expose that, but make the struct private or internal? That's about as close as you'll get.

Put it in its own assembly and have the MyStruct() without the args as a Internal (Friend in VB). Have the Factory in the same assembly as MyStruct() but with a Public accessor.

Now the factory can access the no args MyStruct, but anything calling from outside the assembly must use the Factory.

Edit: My bad, I failed to take into account that this is a struct. You can't do this with a struct, only with a class - in which case, my previous statement stands.

You could make a struct that detects if it is in a default initialized state, and then do something appropriate in that case. I left the Factory in, but a constructor could also be an adequate factory in the common, simple case.

It is a lot of boilerplate code. Since you use D, you may be thinking the same thing I am, "I wish C# had template mixins."

Example:

using System;

namespace CrazyStruct
{
    public struct MyStruct
    {
        private readonly int _height;
        private readonly bool _init; // Will be 'false' using default(MyStruct).

        /// <summary>
        /// Height in centimeters.
        /// </summary>
        public int Height
        {
            get
            {
                if (!_init)
                {
                    // Alternatively, could have the preferred default value set here.
                    // _height = 200; // cm
                    // _heightInit = true;
                    throw new InvalidOperationException("Height has not been initialized.");
                }
                return _height;
            }
            // No set:  immutable-ish.
        }

        private MyStruct(int height)
        {
            _height = height;
            _init = true;
        }

        public static MyStruct Factory(int height)
        {
            return new MyStruct(height);
        }
    }

    static class Program
    {
        static void Main(string[] args)
        {
            MyStruct my = MyStruct.Factory(195);
            Console.WriteLine("My height is {0} cm.", my.Height);
            try
            {
                var array = new MyStruct[1];
                var ms = array[0];
                Console.WriteLine("My height is not {0} cm.", ms.Height);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Caught the expected exception: {0}.", ex);
            }
            Console.ReadKey();
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top