Når du bruker struct?

stemmer
1k

Når bør du bruke struct og ikke klasse i C #? Min konseptuelle modellen er at structs brukes i tider når varen er bare en samling av verdityper . En måte å logisk holde dem alle sammen til en sammenhengende helhet.

Jeg kom over disse reglene her :

  • En struct bør representere en enkelt verdi.
  • En struct bør ha et minne fotavtrykk mindre enn 16 byte.
  • En struct bør ikke endres etter etableringen.

Har disse reglene fungerer? Hva gjør en struct bety semantisk?

Publisert på 06/02/2009 klokken 17:37
kilden bruker
På andre språk...                            


29 svar

stemmer
546

Kilden referert av OP har noen troverdighet ... men hva om Microsoft - hva er den holdning til struct bruk? Jeg søkte litt ekstra læring fra Microsoft , og her er hva jeg fant:

Betrakt som definerer en struktur i stedet for en klasse hvis forekomster av den type som er små og vanligvis kortvarig og er vanligvis innleiret i andre gjenstander.

Ikke definerer en struktur med mindre typen har alle de følgende egenskaper:

  1. Det representerer logisk en enkelt verdi, lik den primitive typer (heltall, dobbel, og så videre).
  2. Den har en forekomst størrelse som er mindre enn 16 byte.
  3. Det er uforanderlig.
  4. Det trenger ikke å være innrammet ofte.

Microsoft bryter konsekvent disse reglene

Ok, # 2 og # 3 uansett. Vår kjære ordlisten har 2 interne structs:

[StructLayout(LayoutKind.Sequential)]  // default for structs
private struct Entry  //<Tkey, TValue>
{
    //  View code at *Reference Source
}

[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Enumerator : 
    IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, 
    IDictionaryEnumerator, IEnumerator
{
    //  View code at *Reference Source
}

* Referansekilde

The 'JonnyCantCode.com' kilde fikk 3 av 4 - ganske tilgivelig siden # 4 sannsynligvis ikke ville være et problem. Hvis du finner deg selv boksing en struct, revurdere din arkitektur.

La oss se på hvorfor Microsoft vil bruke disse structs:

  1. Hver struct, Entryog Enumeratorrepresenterer enkeltverdier.
  2. Hastighet
  3. Entryer aldri sendes som en parameter utsiden av Dictionary klasse. Videre viser undersøkelsen at for å tilfredsstille gjennomføring av IEnumerable bruker ordbok den Enumeratorstruct som kopieres hver gang en enumeratoren er forespurt ... fornuftig.
  4. Internt i ordlisten klassen. Enumeratorer offentlig fordi Dictionary er enumerable og må ha lik tilgjengelighet til grensesnittet implementering IEnumerator - f.eks IEnumerator getter.

Update - I tillegg innse at når en struct implementerer et grensesnitt - som Enumerator gjør - og kastes som gjennomføres typen, blir struct en referansetype, og er flyttet til haugen. Internt i ordlisten klassen, Enumerator er fortsatt en verditype. Men så snart en metodekall GetEnumerator(), en referanse-type IEnumeratorer returnert.

Det vi ikke ser her er ethvert forsøk eller bevis for kravet om å holde structs uforanderlige eller opprettholde en forekomst størrelse på bare 16 bytes eller mindre:

  1. Ingenting i structs ovenfor erklært readonly- ikke uforanderlig
  2. Størrelsen på disse struct kan være godt over 16 byte
  3. Entryhar en ubestemt levetid (fra Add(), til Remove(), Clear()eller renovasjon);

Og ... 4. Begge structs butikken TKey og TValue, som vi alle vet er helt i stand til å være referansetyper (ekstra bonus info)

Hashed nøkler til tross, ordbøker er raske delvis fordi instancing en struct er raskere enn en referansetype. Her har jeg en Dictionary<int, int>som lagrer 300.000 tilfeldige heltall med sekvensielt økes nøkler.

Kapasitet: 312 874
MemSize: 2660827 bytes
Utførte Resize: 5ms
Total tid for å fylle: 889ms

Kapasitet : antall elementer som er tilgjengelige før den indre matrisen må bli endret.

MemSize : bestemt av seriefunksjon ordlisten til en Memory og få en byte lengde (nøyaktig nok for vårt formål).

Fullført Resize : tiden det tar å endre størrelsen på interne utvalg fra 150862 elementer til 312874 elementer. Når du finne at hvert element er sekvensielt kopiert via Array.CopyTo(), som ikke er altfor dårlig.

Total tid for å fylle : riktignok skjevt på grunn av logging og en OnResizehendelse I tilsatt til kilden; imidlertid fortsatt imponerende å fylle 300k heltall mens endring av størrelse 15 ganger under operasjonen. Bare av nysgjerrighet, hva ville den totale tiden for å fylle være hvis jeg allerede visste kapasitet? 13ms

Så nå, hva om Entryvar en klasse? Vil disse tider eller beregninger virkelig skiller seg så mye?

Kapasitet: 312 874
MemSize: 2660827 bytes
Utførte Resize: 26ms
Total tid for å fylle: 964ms

Selvfølgelig er det stor forskjell i endring av størrelse. Enhver forskjell hvis ordbok er initialisert med kapasitet? Ikke nok til å være opptatt av ... 12ms .

Hva som skjer er, fordi Entryer en struct, krever det ikke initialisering som en referansetype. Dette er både skjønnhet og bane av verditypen. For å kunne bruke Entrysom et referansetype, jeg måtte sette inn følgende kode:

/*
 *  Added to satisfy initialization of entry elements --
 *  this is where the extra time is spent resizing the Entry array
 * **/
for (int i = 0 ; i < prime ; i++)
{
    destinationArray[i] = new Entry( );
}
/*  *********************************************** */  

Grunnen til at jeg måtte initial hver matrise element Entrysom et referansetype kan bli funnet på MSDN: Struktur Design . Kort oppsummert:

Ikke gi en standardkonstruktør for en struktur.

Hvis en struktur definerer et standard konstruktør, når matriser med strukturen er opprettet, utføres det felles språk kjøretids automatisk standardkonstruktøren på hvert matriseelement.

Noen kompilatorer, slik som C # kompilatoren, ikke la strukturer for å ha standard konstruktører.

Det er faktisk ganske enkelt, og vi vil låne fra Asimov er tre lover om Robotics :

  1. Struct må være trygt å bruke
  2. Struct må utføre sin funksjon effektivt, uten at dette ville bryte regel # 1
  3. Struct må forbli intakt under bruk med mindre dens ødeleggelse er nødvendig for å tilfredsstille regel # 1

... hva gjør vi tar bort fra dette : kort sagt, være ansvarlig med bruk av verdityper. De er rask og effektiv, men har muligheten til å føre til mange uventede oppførsel hvis ikke skikkelig vedlikeholdt (dvs. utilsiktede kopier).

Svarte 07/08/2011 kl. 13:44
kilden bruker

stemmer
144

Når du ikke trenger polymorfisme, vil verdi semantikk, og unngå haug tildeling og tilhørende søppelrydding overhead. Det forbeholdet er imidlertid at structs (vilkårlig store) er dyrere å passere rundt enn klasse referanser (vanligvis en maskin ord), så klasser kan ende opp med å bli raskere i praksis.

Svarte 06/02/2009 kl. 17:40
kilden bruker

stemmer
134

Jeg er ikke enig med reglene gitt i det opprinnelige innlegget. Her er mine regler:

1) Du bruker structs til ytelse når de oppbevares i arrays. (se også Når er structs svaret? )

2) Du trenger dem i kode passerer strukturerte data til / fra C / C ++

3) Ikke bruk structs med mindre du trenger dem:

  • De oppfører seg forskjellig fra "normale objekter" ( referanse typer ) under oppdraget og ved passering som argumenter, som kan føre til uventede oppførsel; Dette er spesielt farlig hvis personen ser på koden ikke vet at de har å gjøre med en struct.
  • De kan ikke være arvelig.
  • Passerer structs som argumenter er dyrere enn klasser.
Svarte 28/02/2009 kl. 16:33
kilden bruker

stemmer
82

Bruk en struct når du vil ha verdi semantikk i motsetning til referanse semantikk.

Redigere

Ikke sikker på hvorfor folk er downvoting dette, men dette er et gyldig poeng, og ble gjort før op avklart spørsmålet hans, og det er den mest grunnleggende grunnleggende årsaken til en struct.

Hvis du trenger referanse semantikk du trenger en klasse ikke en struct.

Svarte 06/02/2009 kl. 17:40
kilden bruker

stemmer
54

I tillegg til "det er en verdi" svar, en bestemt scenario for bruk structs er når du vet at du har et sett med data som forårsaker søppelrydding problemer, og du har mange gjenstander. For eksempel kan en stor liste / utvalg av Person tilfeller. Den naturlige metafor her er en klasse, men hvis du har stort antall langlivede Person eksempel, kan de ende opp clogging GEN-2 og forårsaker GC boder. Hvis scenariet tilsier det, er en potensiell tilnærming her for å bruke en array (ikke liste) over Person structs , altså Person[]. Nå, i stedet for at millioner av gjenstander i GEN-2, har du en enkelt del på LOH (jeg antar ingen strenger osv her - altså en ren verdi uten referanser). Dette har svært lite GC innvirkning.

Arbeide med disse dataene er vanskelig, så dataene er trolig over-sized for en struct, og du ikke ønsker å kopiere fettverdiene hele tiden. Imidlertid tilgang til det direkte i en matrise kopierer ikke struct - er det på-plass (motsetning til en liste indeksereren, noe som gjør kopi). Det betyr mye arbeid med indekser:

int index = ...
int id = peopleArray[index].Id;

Merk at det å holde verdiene selv uforanderlig vil hjelpe her. For mer komplisert logikk, bruker en fremgangsmåte med en by-ref parameter:

void Foo(ref Person person) {...}
...
Foo(ref peopleArray[index]);

Igjen, dette er på plass - vi har ikke kopiert verdien.

I svært spesifikke scenarier, kan denne taktikken være svært vellykket; men det er en ganske avansert scernario som bør forsøkes bare hvis du vet hva du gjør og hvorfor. Standard her vil være en klasse.

Svarte 22/10/2011 kl. 12:14
kilden bruker

stemmer
36

Fra C # Språk spesifikasjon :

1.7 Structs

Som klasser, structs er datastrukturer som kan inneholde data medlemmer og funksjons medlemmer, men i motsetning til klasser, structs er verdityper og ikke krever haug tildeling. En variabel av en struct typen lagrer direkte dataene fra struct, mens en variabel av en klasse type lagrer en referanse til en dynamisk allokert gjenstand. Struct typer støtter ikke brukerdefinert arv, og alle struct typer implisitt arve fra type objekt.

Structs er spesielt nyttige for små datastrukturer som har verdi semantikk. Komplekse tall, punkter i et koordinatsystem, eller nøkkel-verdi-par i en ordliste er alle gode eksempler på structs. Bruken av structs snarere enn klasser for små datastrukturer kan gjøre en stor forskjell i antallet minnetildeling en applikasjon utfører. For eksempel, danner følgende program og initialiserer en oppstilling av 100 punkter. Med Point implementert som en klasse, er 101 individuelle objekter instansiert-en for matrisen og ett for hvert av de 100 elementer.

class Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

class Test
{
   static void Main() {
      Point[] points = new Point[100];
      for (int i = 0; i < 100; i++) points[i] = new Point(i, i);
   }
}

Et alternativ er å lage Point en struct.

struct Point
{
   public int x, y;

   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}

Nå er det bare ett objekt instansiert-en for de matrise-og punktet tilfeller er lagret i-linje i matrisen.

Struct konstruktører er startet med den nye operatøren, men det betyr ikke at hukommelsen blir tildelt. I stedet for dynamisk allokering av et objekt, og å returnere en referanse til den, en struct konstruktør returnerer ganske enkelt struct verdi i seg selv (typisk i en midlertidig plassering på stabelen), og denne verdien blir deretter kopiert etter behov.

Med klasser, er det mulig for to variabler for å referere til samme gjenstand og dermed mulig for operasjoner på en variabel for å påvirke objektet referert til av den andre variable. Med structs, variablene hver har sin egen kopi av dataene, og det er ikke mulig for operasjoner på en for å påvirke den andre. For eksempel, hvis utgang fremstilles ved følgende kodefragment, avhenger av om punkt er en klasse eller en struct.

Point a = new Point(10, 10);
Point b = a;
a.x = 20;
Console.WriteLine(b.x);

Dersom punktet er en klasse, er den utgang 20 for a og b refererer til det samme objektet. Dersom punktet er en struct, er utgangen 10 på grunn av tilordningen av en til b skaper en kopi av verdien, og denne kopien er upåvirket ved etterfølgende oppgave å øks

Den forrige eksempel fremhever to av begrensningene i structs. Først, å kopiere et helt struct er typisk mindre effektiv enn et objekt kopieres referanse, slik oppgave, og verdien parameteroverføring kan være mer kostbart med structs enn med referansetypene. For det andre, bortsett fra dommeren og ut parametre, er det ikke mulig å lage referanser til structs, som utelukker deres bruk i en rekke situasjoner.

Svarte 17/09/2012 kl. 15:42
kilden bruker

stemmer
31

Structs er bra for atom representasjon av data, hvor de nevnte data kan kopieres flere ganger av koden. Kloning av et objekt er generelt mer kostbart enn å kopiere et struct, som det innebærer å fordele minnet, kjører konstruktøren og allokering / datasanering når det gjøres med den.

Svarte 06/02/2009 kl. 17:58
kilden bruker

stemmer
24

Her er en grunnleggende regel.

  • Hvis alle medlems feltene er verdityper opprette en struct .

  • Hvis noen medlem feltet er en referansetype, opprette en klasse . Dette er fordi referansetypen feltet må haugen tildeling uansett.

Exmaples

public struct MyPoint 
{
    public int X; // Value Type
    public int Y; // Value Type
}

public class MyPointWithName 
{
    public int X; // Value Type
    public int Y; // Value Type
    public string Name; // Reference Type
}
Svarte 22/01/2014 kl. 10:17
kilden bruker

stemmer
17

Først: Interop scenarier eller når du trenger å angi minne layout

Second: Når dataene er nesten samme størrelse som en referanse peker likevel.

Svarte 06/02/2009 kl. 18:12
kilden bruker

stemmer
16

Du må bruke en "struct" i situasjoner hvor du ønsker å eksplisitt angi minne layout med StructLayoutAttribute - typisk for PInvoke.

Edit: Kommentar påpeker at du kan bruke klasse eller struct med StructLayoutAttribute og det er sikkert sant. I praksis vil du vanligvis bruker en struct - det er avsatt på stakken vs haugen som er fornuftig hvis du nettopp passert et argument til en uovervåkede metode samtale.

Svarte 06/02/2009 kl. 18:09
kilden bruker

stemmer
15

Jeg bruker structs for pakking eller utpakking noen form for binær kommunikasjons format. Det inkluderer leser eller skriver til disk, DirectX vertex lister, nettverksprotokoller, eller arbeider med krypterte / komprimerte data.

De tre retningslinjer du listen ikke har vært nyttig for meg i denne sammenhengen. Når jeg trenger å skrive ut fire hundre byte av ting i en bestemt rekkefølge, jeg skal definere en 4-100-byte struct, og jeg skal fylle den med hva som ikke er relatert verdier det er ment å ha, og jeg kommer å sette den opp den måten som er mest fornuftig på den tiden. (Ok, fire hundre bytes ville være ganske strange-- men da jeg skrev Excel-filer for et levende, ble jeg arbeider med structs opp til omtrent førti bytes over, fordi det er hvor stor noen av BIFF postene er.)

Svarte 06/02/2009 kl. 18:25
kilden bruker

stemmer
14

Med unntak av de valuetypes som brukes direkte av runtime og diverse andre for PInvoke formål, bør du bare bruke valuetypes i 2 scenarier.

  1. Når du trenger å kopiere semantikk.
  2. Når du trenger automatisk initialisering, normalt i matriser av disse typene.
Svarte 06/02/2009 kl. 18:15
kilden bruker

stemmer
13

NET støtter value typesog reference types(i Java, kan du definere bare referansetyper). Forekomster av reference typesfå tildelt i forvaltet haug og søppel samles inn når det ikke er noen utestående referanser til dem. Forekomster av value types, på den annen side, er avsatt i stack, og dermed tildelte minnet gjenvinnes så snart deres omfang ender. Og selvfølgelig, value typesbli passert av verdi, og reference typessom referanse. Alle C # primitive datatyper, med unntak av System.String, er verdityper.

Når du bruker struct løpet klasse,

I C #, structser value types, klasser er reference types. Du kan opprette verdityper i C #, ved hjelp av enumsøkeord og structsøkeordet. Ved hjelp av en value typei stedet for en reference typevil resultere i færre objekter på den administrerte haug, noe som resulterer i mindre belastning på søppelsamler (GC), mindre hyppige GC sykluser, og dermed bedre ytelse. Men value typeshar sine ulemper også. Passerer rundt en stor structer definitivt dyrere enn å sende en referanse, det er en opplagt problem. Det andre problemet er den overliggende forbundet med boxing/unboxing. I tilfelle du lurer på hva boxing/unboxingbetyr, følg disse linkene for en god forklaring på boxingog unboxing. Bortsett fra ytelse, det er tider når du bare trenger typer å ha verdi semantikk, som ville være svært vanskelig (eller stygg) å gjennomføre hvis reference typeser alt du trenger. Du bør bruke value typesbare, når du trenger å kopiere semantikk eller trenger automatisk initialisering, normalt i arraysdisse typene.

Svarte 17/09/2012 kl. 11:21
kilden bruker

stemmer
9

Strukturtyper i C # eller andre NET språk er vanligvis brukes til å holde ting som bør oppfører seg som faste store grupper av verdier. En nyttig aspekt av strukturtyper er at de felt av en strukturtypen eksempel kan modifiseres ved å endre lagringssted i hvilket det er holdt, og ikke på noen annen måte. Det er mulig å kode en struktur på en slik måte at den eneste måten å mutere et felt er å konstruere en helt ny forekomst og deretter bruke en struct oppgave å mutere alle feltene i målet ved å overskrive dem med verdier fra det nye eksempel, men med mindre en struct gir ingen midler for å opprette en forekomst hvor dens felt har ikke-standardverdier, vil alle sine felter være foranderlig hvis og hvis struct selv er lagret i en foranderlig sted.

Legg merke til at det er mulig å utforme en struktur type, slik at det vil i hovedsak oppfører seg som en klasse type, dersom strukturen inneholder en privat klasse-type-feltet, og omdirigerer sine egne medlemmer til den av pakket klasse objektet. For eksempel, en PersonCollectionkan tilby eiendommer SortedByNameog SortedById, som begge holder en "uforanderlige" referanse til en PersonCollection(sett i sin konstruktør) og gjennomføre GetEnumeratorved å ringe enten creator.GetNameSortedEnumeratoreller creator.GetIdSortedEnumerator. Slike structs ville oppføre seg omtrent som en referanse til en PersonCollection, bortsett fra at deres GetEnumeratormetoder ville være bundet til forskjellige metoder i PersonCollection. Man kunne også ha en struktur vikle en del av en matrise (for eksempel man kunne definere en ArrayRange<T>struktur som vil holde en T[]heter Arr, en int Offset, og en int Length, med en indeksert egenskap som, for en indeks idxi området 0 til Length-1, ville tilgang til Arr[idx+Offset]) . Dessverre, hvis fooer en skrivebeskyttet forekomst av en slik struktur, aktuelle kompilatoren versjoner vil ikke tillate operasjoner som foo[3]+=4;fordi de har ingen måte å finne ut om slike operasjoner ville forsøke å skrive til felt av foo.

Det er også mulig å designe en struktur for å oppføre en som en verditype som har en variabel størrelse samling (som vises som skal kopieres når struct er), men den eneste måten å gjøre det arbeidet er å sikre at ingen objekt som struct holder en referanse noensinne vil bli utsatt for noe som kan mutere det. For eksempel kan man ha en matrise-lignende struct som holder en privat matrise, og hvis indeksert "put" metoden skaper en ny matrise hvis innhold er som det av den opprinnelige med unntak av en endret element. Dessverre kan det være litt vanskelig å gjøre slike structs utføre effektivt. Mens det er tider som struct semantikk kan være hensiktsmessig (for eksempel å være i stand til å passere en matrise-lignende samling til en rutine, med den anroperen og anropte begge vite at utenfor koden ikke vil endre samling, kan være bedre enn krever både anroperen og callee å forsvare kopiere alle data de er gitt), kravet om at klasse referanser peker til objekter som aldri vil bli muterte er ofte en ganske alvorlig begrensning.

Svarte 24/07/2012 kl. 22:07
kilden bruker

stemmer
8

En struct er et verditype. Hvis du tilordner en struct til en ny variabel, vil den nye variabelen inneholder en kopi av den opprinnelige.

public struct IntStruct {
    public int Value {get; set;}
}

Excecution av de følgende resultater i 5 tilfeller av struct lagret i minnet:

var struct1 = new IntStruct() { Value = 0 }; // original
var struct2 = struct1;  // A copy is made
var struct3 = struct2;  // A copy is made
var struct4 = struct3;  // A copy is made
var struct5 = struct4;  // A copy is made

// NOTE: A "copy" will occur when you pass a struct into a method parameter.
// To avoid the "copy", use the ref keyword.

// Although structs are designed to use less system resources
// than classes.  If used incorrectly, they could use significantly more.

En klasse er en referansetype. Når du tilordner en klasse til en ny variabel inneholder variabelen en referanse til den opprinnelige klassen objektet.

public class IntClass {
    public int Value {get; set;}
}

Excecution av de følgende resultater i bare ett tilfelle av klassen objekt i minnet.

var class1 = new IntClass() { Value = 0 };
var class2 = class1;  // A reference is made to class1
var class3 = class2;  // A reference is made to class1
var class4 = class3;  // A reference is made to class1
var class5 = class4;  // A reference is made to class1  

Struct s kan øke sannsynligheten for en kode feil. Hvis en verdi objekt er behandlet som en foranderlig referanseobjekt, kan en utvikler bli overrasket når endringer blir uventet tapt.

var struct1 = new IntStruct() { Value = 0 };
var struct2 = struct1;
struct2.Value = 1;
// At this point, a developer may be surprised when 
// struct1.Value is 0 and not 1
Svarte 20/07/2017 kl. 18:36
kilden bruker

stemmer
8

Nah - Jeg vet ikke helt enig med reglene. De er gode retningslinjer for å vurdere med ytelse og standardisering, men ikke i lys av mulighetene.

Som du kan se i svarene, er det en logg av kreative måter å bruke dem. Så disse retningslinjene må bare være det, alltid av hensyn til ytelse og effektivitet.

I dette tilfellet bruker jeg klasser for å representere virkelige objekter i sin større form, jeg bruker structs å representere mindre objekter som har mer eksakte bruksområder. Måten du sa det, "en mer sammenhengende helhet." Nøkkelordet er sammenhengende. Klassene vil være flere objektorientert elementer, mens structs kan ha noen av disse egenskaper, deres på en mindre skala. IMO.

Jeg bruker dem mye å sette i Utforsker og Listview-koder der vanlige statiske attributter kan nås raskt. Jeg slite for å få denne informasjonen på en annen måte. For eksempel, i mine databaseapplikasjoner, jeg bruker en Utforsker hvor jeg har Bord, SPs, funksjoner, eller andre gjenstander. Jeg opprette og fylle min struct, legg den i koden, trekk den ut, får data fra utvalget og så videre. Jeg ville ikke gjøre dette med en klasse!

Jeg prøver og holde dem små, bruke dem i enkelt forekomst situasjoner, og holde dem fra å endre. Det er klokt å være klar over minne, tildeling og ytelse. Og testing er så nødvendig.

Svarte 06/02/2009 kl. 19:17
kilden bruker

stemmer
7

Jeg gjorde en liten benchmark med BenchmarkDotNet å få en bedre forståelse av "struct" fordel i tall. Jeg tester gjennomgående sløyfe matrise (eller liste) over structs (eller klasser). Opprette disse matriser eller lister er ute av referanse omfang - det er klart at "klasse" er mer heavy vil utnytte mer minne, og vil innebære GC.

Så konklusjonen er: Vær forsiktig med LINQ og skjulte structs boksing / unboxing og bruker structs for microoptimizations strengt holde med arrays.

PS En annen referanse om bestått struct / klassen gjennom kallstakken er det https://stackoverflow.com/a/47864451/506147

BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 2 (10.0.15063)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233542 Hz, Resolution=309.2584 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.7.2101.1
  Core   : .NET Core 4.6.25211.01, 64bit RyuJIT


          Method |  Job | Runtime |      Mean |     Error |    StdDev |       Min |       Max |    Median | Rank |  Gen 0 | Allocated |
---------------- |----- |-------- |----------:|----------:|----------:|----------:|----------:|----------:|-----:|-------:|----------:|
   TestListClass |  Clr |     Clr |  5.599 us | 0.0408 us | 0.0382 us |  5.561 us |  5.689 us |  5.583 us |    3 |      - |       0 B |
  TestArrayClass |  Clr |     Clr |  2.024 us | 0.0102 us | 0.0096 us |  2.011 us |  2.043 us |  2.022 us |    2 |      - |       0 B |
  TestListStruct |  Clr |     Clr |  8.427 us | 0.1983 us | 0.2204 us |  8.101 us |  9.007 us |  8.374 us |    5 |      - |       0 B |
 TestArrayStruct |  Clr |     Clr |  1.539 us | 0.0295 us | 0.0276 us |  1.502 us |  1.577 us |  1.537 us |    1 |      - |       0 B |
   TestLinqClass |  Clr |     Clr | 13.117 us | 0.1007 us | 0.0892 us | 13.007 us | 13.301 us | 13.089 us |    7 | 0.0153 |      80 B |
  TestLinqStruct |  Clr |     Clr | 28.676 us | 0.1837 us | 0.1534 us | 28.441 us | 28.957 us | 28.660 us |    9 |      - |      96 B |
   TestListClass | Core |    Core |  5.747 us | 0.1147 us | 0.1275 us |  5.567 us |  5.945 us |  5.756 us |    4 |      - |       0 B |
  TestArrayClass | Core |    Core |  2.023 us | 0.0299 us | 0.0279 us |  1.990 us |  2.069 us |  2.013 us |    2 |      - |       0 B |
  TestListStruct | Core |    Core |  8.753 us | 0.1659 us | 0.1910 us |  8.498 us |  9.110 us |  8.670 us |    6 |      - |       0 B |
 TestArrayStruct | Core |    Core |  1.552 us | 0.0307 us | 0.0377 us |  1.496 us |  1.618 us |  1.552 us |    1 |      - |       0 B |
   TestLinqClass | Core |    Core | 14.286 us | 0.2430 us | 0.2273 us | 13.956 us | 14.678 us | 14.313 us |    8 | 0.0153 |      72 B |
  TestLinqStruct | Core |    Core | 30.121 us | 0.5941 us | 0.5835 us | 28.928 us | 30.909 us | 30.153 us |   10 |      - |      88 B |

Kode:

[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkRef
    {
        public class C1
        {
            public string Text1;
            public string Text2;
            public string Text3;
        }

        public struct S1
        {
            public string Text1;
            public string Text2;
            public string Text3;
        }

        List<C1> testListClass = new List<C1>();
        List<S1> testListStruct = new List<S1>();
        C1[] testArrayClass;
        S1[] testArrayStruct;
        public BenchmarkRef()
        {
            for(int i=0;i<1000;i++)
            {
                testListClass.Add(new C1  { Text1= i.ToString(), Text2=null, Text3= i.ToString() });
                testListStruct.Add(new S1 { Text1 = i.ToString(), Text2 = null, Text3 = i.ToString() });
            }
            testArrayClass = testListClass.ToArray();
            testArrayStruct = testListStruct.ToArray();
        }

        [Benchmark]
        public int TestListClass()
        {
            var x = 0;
            foreach(var i in testListClass)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestArrayClass()
        {
            var x = 0;
            foreach (var i in testArrayClass)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestListStruct()
        {
            var x = 0;
            foreach (var i in testListStruct)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestArrayStruct()
        {
            var x = 0;
            foreach (var i in testArrayStruct)
            {
                x += i.Text1.Length + i.Text3.Length;
            }
            return x;
        }

        [Benchmark]
        public int TestLinqClass()
        {
            var x = testListClass.Select(i=> i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }

        [Benchmark]
        public int TestLinqStruct()
        {
            var x = testListStruct.Select(i => i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }
    }
Svarte 24/07/2017 kl. 09:16
kilden bruker

stemmer
6

En klasse er en referansetype. Når et objekt av klassen er opprettet, den variable som objektet er tildelt til innehar bare en henvisning til at minnet. Når objektreferansen er tildelt en ny variabel, refererer den nye variable til det opprinnelige objektet. Endringer som gjøres i en variabel, gjenspeiles i den andre variable, fordi de begge refererer til de samme data. En struct er et verditype. Når en struct er opprettet, variabelen som struct er tildelt holder struct faktiske data. Når struct er tildelt en ny variabel, blir den kopiert. Den nye variable og den opprinnelige variable inneholder derfor to separate kopier av de samme data. Endringer som gjøres i ett eksemplar, påvirker ikke den andre kopien. Generelt blir klasser anvendes for å modellere mer kompleks oppførsel, eller data som er ment å bli modifisert etter en klasse objekt blir opprettet. Structs er best egnet for små datastrukturer som inneholder først og fremst data som ikke er ment til å bli endret etter at struct er opprettet.

Klasser og Structs (C # Programming Guide)

Svarte 23/05/2014 kl. 09:52
kilden bruker

stemmer
6

Min regel er

1 Bruk alltid klasse;

2, Hvis det er noen ytelsesproblem, jeg prøver å endre noen klasse å konstruere avhengig av reglene som @IAbstract nevnt, og deretter gjøre en test for å se om disse endringene kan forbedre ytelsen.

Svarte 30/08/2013 kl. 08:22
kilden bruker

stemmer
4

Jeg var bare å gjøre med Windows Communication Foundation [WCF] navngitt data og jeg la merke til at det gir mening å bruke Structs for å sikre at utveksling av data er av verditypen i stedet for referansetype .

Svarte 27/03/2016 kl. 22:16
kilden bruker

stemmer
4

Jeg tror en god første tilnærming er "aldri".

Jeg tror en god andre tilnærming er "aldri".

Hvis du er desperat etter perf, vurdere dem, men da alltid måle.

Svarte 06/02/2009 kl. 17:44
kilden bruker

stemmer
2

C # struct er et lett alternativ til en klasse. Det kan gjøre nesten det samme som en klasse, men det er mindre "dyrt" for å bruke en struct snarere enn en klasse. Grunnen til dette er litt teknisk, men for å oppsummere, er nye tilfeller av en klasse plassert på haugen, der nylig startes structs er plassert på stakken. Videre er du ikke arbeider med referanser til structs, som med klasser, men i stedet du jobber direkte med struct eksempel. Dette betyr også at når du passerer en struct til en funksjon, er det av verdi, i stedet for som en referanse. Det er mer om dette i kapittelet om funksjonsparametre.

Så, bør du bruke structs når du ønsker å representere mer enkle datastrukturer, og spesielt hvis du vet at du vil være forekomster mange av dem. Det er mange eksempler i .NET rammeverket, hvor Microsoft har brukt structs stedet for klasser, for eksempel Point, rektangel og Color struct.

Svarte 24/10/2017 kl. 13:15
kilden bruker

stemmer
2

Kort fortalt, bruker struct dersom:

1- dine objektegenskaper / felt trenger ikke å endres. Jeg mener du bare ønsker å gi dem en initiell verdi og deretter lese dem.

2- egenskaper og felt i objektet er verditype, og de er ikke så store.

Hvis det er tilfelle kan du dra nytte av structs for en bedre ytelse og optimalisert minnetildeling som de bruker bare stabler snarere enn både stabler og hauger (i klasser)

Svarte 26/05/2016 kl. 14:31
kilden bruker

stemmer
2

Struktur eller verdityper kan brukes i følgende scenarier -

  1. Hvis du ønsker å hindre at objektet som skal samles inn av søppelrydding.
  2. Hvis det er en enkel type og ingen medlemsfunksjonen modifiserer dets forekomstfelter
  3. Hvis det ikke er noe behov for å utlede fra andre typer eller er avledet til andre typer.

Du kan få vite mer om verdityper og verdier typer her på denne lenken

Svarte 31/08/2015 kl. 08:50
kilden bruker

stemmer
2

Struct kan brukes til å forbedre søppelrydding ytelse. Selv om du vanligvis ikke trenger å bekymre deg for GC ytelse, er det situasjoner der det kan være en morder. Som store cacher i lav latency applikasjoner. Se dette innlegget for et eksempel:

http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/

Svarte 03/07/2013 kl. 14:20
kilden bruker

stemmer
1

MYTE # 1: structs er lette KLASSER

Denne myten kommer i en rekke former. Noen mennesker tror at verdityper ikke kan eller bør ikke har metoder eller andre betydelige atferds de bør brukes som enkle dataoverføringstyper, med bare offentlige felt eller enkle egenskaper. Datetime typen er en god moteksempel til dette: det er fornuftig at det skal være en verditype, i form av å være en grunnleggende enhet som et tall eller en bokstav, og det gjør også fornuftig for at det skal kunne utføre beregninger basert på sin verdi. Se på ting fra den andre retningen, bør dataoverføring typer ofte være referansetypene likevel-the beslutning bør være basert på ønsket verdi eller referanse typen semantikk, ikke enkelhet av type. Andre mennesker tror at verditypene er “lettere” enn referansetyper i forhold til ytelse. Sannheten er at i noen tilfeller verdityper er mer performant- de ikke krever søppelrydding mindre de er innestengt, ikke har den type identifisering overhead, og ikke krever dereferencing, for eksempel. Men på andre måter, referansetyper er mer performant-parameteroverføring, å tilordne verdier til variabler, returnerer verdier, og lignende operasjoner krever bare 4 eller 8 byte til becopied (avhengig av om man kjører en 32-bit eller 64-bit CLR ) i stedet for å kopiere alle data. Tenk om Arraylist var liksom en “ren” verditype, og passerer en Arraylist uttrykk til en metode som er involvert kopiere alle sine data! I nesten alle tilfeller, er ytelsen ikke egentlig bestemt av denne typen avgjørelse uansett. Flaskehalser er nesten aldri der du tror de vil være, og før du gjør et design beslutning basert på resultater, bør du måle de ulike alternativene. Det er verdt å merke seg at kombinasjonen av de to oppfatninger ikke fungerer heller. Det spiller ingen rolle hvor mange metoder en type har (enten det er en klasse eller en struct) -den minne tatt per forekomst er ikke berørt. (Det er en kostnad i form av minne tatt opp for selve koden, men det er påløpt en gang i stedet for hver forekomst.)

Myte # 2: referanse typer LIVE PÅ haugen; Verdityper LIVE på stakken

Denne er ofte forårsaket av latskap på den delen av personen gjenta det. Den første delen er korrekt-en forekomst av en referansetypen er alltid opprettes på haugen. Det er den andre delen som fører til problemer. Som jeg allerede har nevnt, lever en variabel verdi uansett hvordan den er deklarert, så hvis du har en klasse med en forekomst variabel av type int, vil den variabelen verdi for en gitt objekt alltid være der resten av dataene for objektet er- på haugen. Bare lokale variabler (variabler innenfor det vises til metoder) og fremgangsmåte parametere leve på stabelen. I C # 2 og senere, med noen lokale variabler ikke egentlig bor på stakken, som du ser når vi ser på anonyme metoder i kapittel 5. ER disse konseptene relevant nå? Det er hevdes at hvis du skriver forvaltet kode, bør du la runtime bekymring om hvordan hukommelsen fungerer best. Faktisk gjør språkspesifikasjonen ingen garantier for hva som bor der; en fremtidig runtime kan være i stand til å skape noen objekter på stakken hvis den vet det kan komme unna med det, eller C # kompilatoren kan generere kode som knapt bruker stabelen i det hele tatt. Den neste myten er vanligvis bare en terminologi problem.

Myte # 3: objekter sendes ved referanse i C # SOM STANDARD

Dette er trolig den mest spredte myte. Igjen, folk som gjør denne påstanden ofte (men ikke alltid) vet hvordan C # faktisk oppfører seg, men de vet ikke hva “passerer referanse” egentlig betyr. Dessverre er dette forvirrende for folk som vet hva det betyr. Den formelle definisjonen av pass ved referanse er relativt komplisert, med l-verdier og lignende datavitenskap terminologi, men det viktigste er at hvis du passerer en variabel som referanse, kan metoden du ringer endre verdien av den som ringer variable ved å endre parameterverdien. Husk nå at verdien av en referansetype variabelen er referansen, ikke selve objektet. Man kan endre innholdet av objektet som en parameter refererer til uten parameter selv overføres ved referanse. For eksempel endres den følgende metode innholdet av Stringbuilder objekt, men uttrykket anroperens vil fortsatt vise til den samme gjenstand som før:

void AppendHello(StringBuilder builder)
{
    builder.Append("hello");
}

Når denne metoden kalles, blir parameterverdien (en referanse til en Stringbuilder) forbi verdi. Hvis du skulle endre verdien av byggmester variable innenfor metode, for eksempel i utsagnet byggmester = null; -som endringen ikke ville bli sett av den som ringer, i motsetning til myten. Det er interessant å merke seg at ikke bare er “med referanse” bit av myten unøyaktig, men så er de “objektene er bestått” bit. Objekter selv aldri gått, enten ved henvisning eller verdi. Når en referansetypen er involvert, enten den variable føres ved referanse, eller verdien av argumentet (referanse) er ført av verdi. Bortsett fra noe annet, svarer dette på spørsmålet om hva som skjer når null brukes som en by-verdi argument-hvis gjenstandene ble sendt rundt, som ville føre til problemer, da det ikke ville være et objekt for å passere! I stedet er det null referansen vedtatt av verdi på samme måte som enhver annen referanse ville være. Hvis dette rask forklaring har forlatt deg forvirret, kan det være lurt å se på min artikkel, “Parameter passerer i C #,” ( http://mng.bz/otVt ), som går inn i mye mer detalj. Disse mytene er ikke de eneste rundt. Boksing og unboxing komme inn på sin rettmessige del av misforståelser, som jeg skal prøve å rydde opp etterpå.

Annonse: C # Dybde tredje Versjon av Jon Skeet

Svarte 27/02/2019 kl. 16:39
kilden bruker

stemmer
1

Jeg bruker sjelden en struct for ting. Men det er bare meg. Det kommer an på om jeg trenger objektet som kunne ha nullverdier eller ikke.

Som nevnt i andre svar, jeg bruker klasser for virkelige stedene. Jeg har også tenkemåte av structs brukes til lagring av små datamengder.

Svarte 06/02/2009 kl. 19:21
kilden bruker

stemmer
-8

Det virker for meg at struct har ingen sterk semantisk som gir brukeren en sterk idé om når du skal bruke den.

Det ligner som en klasse, men innsjøen meste av sin funksjon. Det er en slags degradert versjon av en klasse. Det er mye sier da ikke bruke det, men svært få på når du skal bruke den.

IMO, er det ingen grunn til at struct skal gjennomføres i et OO språk i første omgang. Egentlig primitive typen bør ikke eksistere i en ren OO språk, men jeg komme bort fra emnet.

Det kan være en måte å optimalisere ting. En slags boksing gratis ting som kan optimalisere på noen kaller stedet.

Min 2 prosent, vil jeg si at det brøt KISS av språket prinsippet og unngå det så mye har jeg kan.

Svarte 15/10/2014 kl. 17:27
kilden bruker

stemmer
-9

Strukturer er i de fleste måter som klasser / objekter. Strukturen kan inneholde funksjoner, medlemmer og kan være arvelig. Men strukturer er i C # brukes bare for data holde . Strukturer ikke ta mindre RAM enn klasser og er lettere for søppel samler å samle . Men når du bruker funksjonene i strukturen, så kompilatoren faktisk hadde struktur svært likt som klasse / objekt, så hvis du vil ha noe med funksjoner, og deretter bruke klasse / objekt .

Svarte 20/11/2014 kl. 20:02
kilden bruker

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