Pregunta

Tengo una estructura C # donde necesito prohibir llamar al constructor sin args.

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

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

Estoy haciendo esto principalmente para forzar valores explícitos para todos los miembros ya que no hay valores predeterminados válidos y todos los miembros deben tener valores válidos.

En C ++ esto sería fácil, agregue un constructor privado pero C # no lo permite.

¿Hay alguna manera de prevenir lo anterior?

Realmente necesito imponer el uso de una fábrica para evitar que todas las llamadas de constructor público funcionen igual de bien.


Revelador completo: para evitar una dependencia mono, la aplicación C # se está traduciendo automáticamente a D donde new Struct () da como resultado un puntero y esto me está arruinando las cosas. Sin embargo, esta pregunta es relevante a pesar de eso, así que simplemente ignórela.

¿Fue útil?

Solución

No puedes. Todas las estructuras tienen un constructor público sin parámetros por definición en C #. (En el CLR, casi ninguno lo hace, pero siempre pueden actuar como si lo hubieran hecho). Consulte esta pregunta para saber por qué puede hacerlo. No defina sus propios constructores sin parámetros en estructuras (en C #, de todos modos).

De hecho, puede puede evitar esta declaración si le agrada escribir su tipo de valor en IL. Acabo de comprobarlo, y si se asegura de que su tipo de valor solo tiene un constructor sin parámetros, y lo hace interno, no podrá escribir MyStruct ms = new MyStruct (); Pero esto no impide:

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

que funciona alrededor de la nueva restricción, por lo que realmente no te compra nada. Eso es bastante bueno, ya que jugar en IL sería desordenado.

¿Estás seguro de que realmente quieres escribir una estructura en primer lugar? Eso es casi nunca es una buena idea.

Otros consejos

No puedes.

Todos los valores en una estructura deben inicializarse en el momento de la construcción, y no hay forma de hacerlo fuera del constructor.

¿Qué estás tratando de lograr exactamente haciendo eso? Las estructuras son tipos de valor, por lo que obtendrá un " nuevo " estructura para la mayoría de las operaciones. Será muy difícil imponer el tipo de restricciones para las que usaría una fábrica en una estructura.

Cualquiera puede crear una estructura en cualquier momento sin llamar a un constructor, siempre que tenga acceso a la estructura. Piénselo de esta manera:

Si crea una matriz de objetos con 1000 elementos, todos se inicializan como nulos, por lo que no se llama a los constructores.

Con las estructuras, no existe el nulo. Si crea una matriz con 1000 objetos DateTime, todos se inicializan a cero, lo que equivale a DateTime.Min. Los diseñadores del tiempo de ejecución decidieron hacerlo para que pudieras crear una serie de estructuras sin llamar al constructor N veces: un éxito que muchas personas no se darían cuenta de que estaba allí.

Sin embargo, su idea de fábrica es buena. ¿Satisfacería sus necesidades de crear una interfaz y exponer eso, pero hacer que la estructura sea privada o interna? Eso es lo más cerca que estarás.

Póngalo en su propio ensamblado y tenga MyStruct () sin los argumentos como interno (amigo en VB). Tenga Factory en el mismo ensamblado que MyStruct () pero con un descriptor de acceso público.

Ahora la fábrica puede acceder a MyStruct sin argumentos, pero cualquier cosa que llame desde fuera del ensamblaje debe usar la Fábrica.

Editar: Mi error, no pude tener en cuenta que esto es una estructura. No puede hacer esto con una estructura, solo con una clase, en cuyo caso, mi declaración anterior se mantiene.

Podría hacer una estructura que detecte si está en un estado inicializado predeterminado y luego hacer algo apropiado en ese caso. Dejé la Fábrica adentro, pero un constructor también podría ser una fábrica adecuada en el caso común y simple.

Es mucho código repetitivo. Como usa D, es posible que esté pensando lo mismo que yo, "Me gustaría que C # tuviera mixins de plantilla".

Ejemplo:

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();
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top