Kan structs inneholde felt av referansetyper

stemmer
46

Kan structs inneholde felt av referansetyper? Og hvis de kan er dette en dårlig praksis?

Publisert på 03/06/2009 klokken 16:19
kilden bruker
På andre språk...                            


6 svar

stemmer
73

Ja de kan. Er det en god idé? Vel, det avhenger av situasjonen. Personlig har jeg sjelden lage min egen structs i første omgang ... Jeg ville behandle noen ny brukerdefinert struct med en viss grad av skepsis. Jeg er ikke noe som tilsier at det er alltid feil alternativ, bare at det er behov for mer av et klart argument enn en klasse.

Det ville være en dårlig idé for en struct å ha en referanse til en foranderlig objekt selv ... ellers kan du ha to verdier som ser uavhengig, men ikke:

MyValueType foo = ...;
MyValueType bar = foo; // Value type, hence copy...

foo.List.Add("x");
// Eek, bar's list has now changed too!

Foranderlig structs er onde. Uforanderlige structs med referanser til foranderlig typer er sneakily onde på forskjellige måter.

Svarte 03/06/2009 kl. 16:26
kilden bruker

stemmer
17

Sure ting og det er ikke dårlig praksis å gjøre det.

struct Example {
  public readonly string Field1;
}

Den skrivebeskyttet er ikke nødvendig, men det er lurt å gjøre struct uforanderlige.

Svarte 03/06/2009 kl. 16:22
kilden bruker

stemmer
3

Ja, det er mulig, og ja, det er som regel en dårlig praksis.

Hvis du ser på .NET rammeverket i seg selv, vil du se nesten alle structs inneholde primitive verdityper alene.

Svarte 03/06/2009 kl. 16:25
kilden bruker

stemmer
2

Grunnen til at du ikke kan ha foranderlig structs er på grunn av behavoir referansetyper. Les denne artikkelen: http://www.yoda.arachsys.com/csharp/parameters.html

Når du har en struct som inneholder et objekt (noe som ikke er en primitiv som int eller dobbel) og du kopierer en forekomst av struct, innsiden er objektet ikke er "dype" kopiert, fordi det er rett og slett en referanse (peker ) til et minneområde som inneholder den aktuelle klassen. Så hvis du kopierer en foranderlig struct som inneholder klasse tilfeller vil kopien bli henviser de samme instanser som originalen (Derav bar liste blir endret over).

Hvis du absolutt må ha struct være foranderlig, gjøre eventuelle klasse tilfeller inne skrivebeskyttet, eller - dette er dårlig praksis - prøv å sikre at du aldri gjør en kopi av struct.

Svarte 11/02/2011 kl. 14:03
kilden bruker

stemmer
2

Ja de kan.

Det kommer an på.

Mange holder holdning som en struct skal være uforanderlig, og i dette tilfellet, kan holde en referanse til et objekt mener det er det ikke.

Men det avhenger av situasjonen.

Svarte 03/06/2009 kl. 16:21
kilden bruker

stemmer
0

Siden dette fikk en downvote, jeg prøver å skrive litt for å se om det kan bli klarere. Dette spørsmålet er gammel; men bra! Jeg har nylig kom også over noen linker som utdyper dette.

Poenget jeg prøver å legge til er at hvis du gjør erklærer referansefelt, må du være i stand til å resonnere utenfor ditt eget block: når noen bruker din struct. Den spesifikke punktet jeg lagt var egentlig bare om å erklære en skrivebeskyttet felt av en struct; men i så fall kan de feltene du har i din struct endre sine resultater; og det er vanskelig å resonnere om.

Jeg kom over denne linken, hvor programmereren erklært en klasse som inneholder en readonly structfelt. Den feltet i sin klasse er en struct--- det er en LinkedList<T>.Enumerator- og det brøt fordi feltet er readonly--- sine egne klassemetoder få en kopi av enumeratoren struct og staten er kopiert og ikke dynamisk.

Men , hvis du går videre og fikse hans koden ved ganske enkelt å fjerne readonlyfra struct-feltet (som fungerer ); og da men , hvis du bestemmer deg for å lage din egen klasse en struct, nå igjen , en forbruker av din struct kan ikke bruke det som en skrivebeskyttet felt eller annet de i sin tur bli bitt av det samme problemet. (Og hvis dette virker contrived fordi du ikke vil ha en skrivebeskyttet enumeratoren, du faktisk kan om den støtter Reset!)

Så hvis det ikke er det klareste eksempelet, poenget jeg gjør er at du kan resonnere om din egen implementering, men hvis du er en struct du må også grunnen om forbrukere som kopierer din verdi og hva de får.

Eksemplet i fant er knyttet nedenfor.

Hans klasse er ikke en struct, men gjør inneholder m_Enumerator feltet (og programmerer vet visstnok at det er en struct).

Det viser seg at denne klassens metoder få en kopi av denne verdien, og ikke fungerer. --- Du kan faktisk undersøke denne blokken svært nøye for å forstå det.

Du kan fikse det ved å gjøre feltet ikke readonly --- som allerede peker på forvirrende. Men du kan også fikse det ved å erklære feltet som interfacetypen --- IEnumerator<int>.

Men , hvis du gjør fikse det ved å la feltet erklært som structog erklære det ikke readonly, og deretter velge å definere klassen som en struct, så hvis noen erklærer en forekomst av ditt struct som et readonlyfelt i noen klasse, igjen de taper!

EG:

public class Program
{
    private struct EnumeratorWrapper : IEnumerator<int>
    {
        // Fails always --- the local methods read the readonly struct and get a copy
        //private readonly LinkedList<int>.Enumerator m_Enumerator;

        // Fixes one: --- locally, methods no longer get a copy;
        // BUT if a consumer of THIS struct makes a readonly field, then again they will
        // always get a copy of THIS, AND this contains a copy of this struct field!
        private LinkedList<int>.Enumerator m_Enumerator;

        // Fixes both!!
        // Because this is not a value type, even a consumer of THIS struct making a
        // readonly copy, always reads the memory pointer and not a value
        //private IEnumerator<int> m_Enumerator;


        public EnumeratorWrapper(LinkedList<int> linkedList) 
            => m_Enumerator = linkedList.GetEnumerator();


        public int Current
            => m_Enumerator.Current;

        object System.Collections.IEnumerator.Current
            => Current;

        public bool MoveNext()
            => m_Enumerator.MoveNext();

        public void Reset()
            => ((System.Collections.IEnumerator) m_Enumerator).Reset();

        public void Dispose()
            => m_Enumerator.Dispose();
    }


    private readonly LinkedList<int> l = new LinkedList<int>();
    private readonly EnumeratorWrapper e;


    public Program()
    {
        for (int i = 0; i < 10; ++i) {
            l.AddLast(i);
        }
        e = new EnumeratorWrapper(l);
    }


    public static void Main()
    {
        Program p = new Program();

        // This works --- a local copy every time
        EnumeratorWrapper e = new EnumeratorWrapper(p.l);
        while (e.MoveNext()) {
            Console.WriteLine(e.Current);
        }

        // This fails if the struct cannot support living in a readonly field
        while (p.e.MoveNext()) {
            Console.WriteLine(p.e.Current);
        }
        Console.ReadKey();
    }
}

Hvis du deklarerer en structmed et interfacefelt, vil du ikke vet hva du har der, men likevel kan du faktisk resonnere mer om hva du får når du bare referere til den! Dette er veldig interessant; men bare fordi språket gjør at så mange friheter med struct: du må starte fra noe veldig enkelt; og bare legge hva du konkret kan resonnere om!

Ett poeng er at referansen også sier du bør definere standardverdiene som rimelig; som ikke er mulig med en referanse feltet! Hvis du ved et uhell påberope standardkonstruktør --- alltid mulig med en struct--- så får du et null referanse.

En siste kommentar også. Mange folk forsvare foranderlig structs og store foranderlig structs. Men hvis du peer inn, du vanligvis oppdage at de er rett og slett scoping disse objektene på en måte som tillater dem å finitely grunn om atferd, og structs ikke lekke inn scopes hvor de invarianter kan endres.

... For mange mennesker begynner å forklare structs som "Akkurat som en klasse, men ... x, y, z, 1, 2, alfa, beta disco". Det må forklares som et par skrivebeskyttet verdier; periode; bortsett fra nå som du vet noe, kan du begynne å grunn om å legge til noe!

Eksempelet jeg kom accros er her:

https://www.red-gate.com/simple-talk/blogs/why-enumerator-structs-are-a-really-bad-idea/

Svarte 27/11/2017 kl. 14:21
kilden bruker

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