Hvordan håndheve jeg å bruke en fabrikk på en struct i C #

stemmer
3

Jeg har ac # struct hvor jeg trenger å forby ringer ingen args konstruktøren på den.

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

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

Jeg gjør dette mest for å tvinge explicet verdier for alle medlemmer så det er ingen gyldige standardverdier og alle medlemmer må ha gyldige verdier.

I C ++ dette ville være lett, legge til en privat konstruktør men c # tillater ikke det.

Er det en måte å forhindre de ovenfor?

Jeg trenger virkelig å håndheve ved hjelp av en fabrikk, så hindrer alle offentlige konstruktør samtaler ville fungere like bra.


Full Formidleren: for å unngå en mono avhengighet, er C # programmet blir automatisk oversatt til D der new Struct()resulterer i en peker, og dette er rote ting opp for meg. Men dette spørsmålet er relevant til tross for at så bare overse det.

Publisert på 04/12/2008 klokken 20:33
kilden bruker
På andre språk...                            


5 svar

stemmer
11

Du kan ikke. Alle structs har en offentlig parameterless konstruktør per definisjon i C #. (I CLR nesten ingen av dem gjør, men de kan alltid opptre som om de har.) Se på dette spørsmålet for hvorfor du ikke kan definere dine egne parameterless konstruktører på structs (i C #, uansett).

Faktisk, du kan forebygge denne påstanden hvis du er fornøyd med å skrive din verditype i IL. Jeg har nettopp sjekket, og hvis du sørge for at verdien skriver bare har en parameterless konstruktør, og gjøre det internt, vil du ikke være i stand til å skrive MyStruct ms = new MyStruct();, men dette hindrer ikke:

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

som fungerer rundt den nye begrensningen - så det gjør egentlig ikke kjøpe deg noe. Det er ganske bra egentlig, så rote rundt i IL ville være rotete.

Er du sikker på at du virkelig ønsker å være å skrive en struct i første omgang? Det er nesten aldri en god idé.

Svarte 04/12/2008 kl. 20:37
kilden bruker

stemmer
3

Du kan ikke.

Alle verdier i en struct må startes på byggetiden, og det er ingen måte å gjøre det utenfor konstruktøren.

Hva er det du prøver å oppnå ved å gjøre det? Structs er verdityper, slik at du får en "ny" struct for de fleste operasjoner. Det vil være svært vanskelig å håndheve den slags begrensninger du vil bruke en fabrikk for på en struct.

Svarte 04/12/2008 kl. 20:38
kilden bruker

stemmer
2

Hvem som helst kan opprette en struct når som helst uten å kalle en konstruktør, så lenge de har tilgang til struct. Tenk på det på denne måten:

Hvis du oppretter en rekke objekter med 1000 elementer, de alle får initialisert til null, slik at ingen konstruktører kalles.

Med structs, det er ikke noe slikt som null. Hvis du oppretter en matrise med 1000 Datetime gjenstander, de er alle initialisert til null, noe som tilsvarer DateTime.Min. Designerne av runtime valgte å gjøre det slik at du kan skape en rekke structs uten å kalle konstruktøren N ganger - en forestilling rammet mange mennesker ikke ville innse var der.

Din fabrikken idé er en god en, skjønt. Ville det møte dine behov for å lage et grensesnitt og utsetter det, men gjør struct private eller interne? Det er omtrent så nær som du får.

Svarte 04/12/2008 kl. 20:40
kilden bruker

stemmer
0

Du kan lage en struct som oppdager om det er i en standard initialisert tilstand, og deretter gjøre noe riktig i dette tilfellet. Jeg forlot fabrikken i, men en konstruktør kan også være en tilstrekkelig fabrikk i felles, enkel sak.

Det er mye av standardkode. Siden du bruker D, kan du bli tenker det samme er jeg, "Jeg skulle ønske C # hadde mal mixins."

Eksempel:

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();
        }
    }
}
Svarte 29/01/2016 kl. 15:32
kilden bruker

stemmer
0

Sett det i sin egen montering og har MyStruct () uten args som en intern (venn i VB). Har Factory i samme montasje som MyStruct (), men med en offentlig tilbehør.

Nå fabrikken kan få tilgang til noen args MyStruct, men noe ringer fra utenfor menigheten må bruke Factory.

Edit: Min dårlig, jeg klarte ikke å ta i betraktning at dette er en struct. Du kan ikke gjøre dette med en struct, bare med en klasse - og i så fall, står min tidligere uttalelse.

Svarte 04/12/2008 kl. 20:37
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more