Domanda

Ho una struttura c # in cui devo proibire di chiamare il costruttore no args su di esso.

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

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

Lo sto facendo principalmente per forzare i valori espliciti per tutti i membri in quanto non esistono valori predefiniti validi e tutti i membri devono avere valori validi.

In C ++ questo sarebbe facile, aggiungi un costruttore privato ma c # non lo consente.

C'è un modo per impedire quanto sopra?

Ho davvero bisogno di imporre l'uso di una fabbrica, quindi impedire che tutte le chiamate del costruttore pubblico funzionino altrettanto bene.


Divulgatore completo: per evitare una dipendenza mono, l'app c # viene automaticamente tradotta in D dove new Struct () si traduce in un puntatore e questo mi fa confondere le cose. Tuttavia, questa domanda è rilevante nonostante ciò, quindi ignorala.

È stato utile?

Soluzione

Non puoi. Tutte le strutture hanno un costruttore pubblico senza parametri per definizione in C #. (Nel CLR quasi nessuno di loro lo fa, ma possono sempre agire come se lo fossero.) Vedi questa domanda per sapere perché puoi definisci i tuoi costruttori senza parametri sulle strutture (in C #, comunque).

In effetti, puoi impedire questa affermazione se sei felice di scrivere il tuo tipo di valore in IL. Ho appena controllato e se ti assicuri che il tuo tipo di valore abbia solo un costruttore senza parametri e lo rendi interno, non sarai in grado di scrivere MyStruct ms = new MyStruct (); Ma questo non impedisce:

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

che aggira la nuova restrizione, quindi non ti compra nulla. È abbastanza buono davvero, dato che fare casino in IL sarebbe disordinato.

Sei sicuro di voler davvero scrivere una struttura in primo luogo? È quasi mai una buona idea.

Altri suggerimenti

Non puoi.

Tutti i valori in una struttura devono essere inizializzati al momento della costruzione e non c'è modo di farlo al di fuori del costruttore.

Cosa stai esattamente cercando di fare facendo questo? Le strutture sono tipi di valore, quindi otterrai un " nuovo " struct per la maggior parte delle operazioni. Sarà molto difficile imporre il tipo di vincoli per i quali utilizzeresti una factory su una struttura.

Chiunque può creare una struttura in qualsiasi momento senza chiamare un costruttore, purché abbiano accesso alla struttura. Pensaci in questo modo:

Se crei una matrice di oggetti con 1000 elementi, vengono tutti inizializzati su null, quindi non vengono chiamati costruttori.

Con le strutture, non esiste nulla come null. Se si crea un array con 1000 oggetti DateTime, vengono tutti inizializzati su zero, che equivale a DateTime.Min. I progettisti del runtime hanno scelto di realizzarlo in modo da poter creare una serie di strutture senza chiamare N il costruttore N volte - un colpo di prestazione che molte persone non avrebbero realizzato che fosse lì.

Tuttavia, la tua idea di fabbrica è buona. Soddisfarebbe le tue esigenze per creare un'interfaccia ed esporla, ma rendere la struttura privata o interna? Questo è il più vicino possibile.

Mettilo nel suo assembly e disponi MyStruct () senza l'args come Internal (Friend in VB). Avere Factory nello stesso assembly di MyStruct () ma con un accesso pubblico.

Ora la fabbrica può accedere a MyStruct no args, ma tutto ciò che chiama dall'esterno dell'assemblea deve usare la fabbrica.

Modifica: Mio male, non sono riuscito a tenere conto del fatto che si tratta di una struttura. Non puoi farlo con uno struct, solo con una classe - nel qual caso, la mia precedente affermazione è valida.

È possibile creare una struttura che rilevi se si trova in uno stato inizializzato predefinito, quindi fare qualcosa di appropriato in quel caso. Ho lasciato la fabbrica, ma un costruttore potrebbe anche essere una fabbrica adeguata nel caso semplice e comune.

È un sacco di codice del boilerplate. Dato che usi D, potresti pensare la stessa cosa che sono io, "Vorrei che C # avesse dei template mixin."

Esempio:

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();
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top