Structuren versus klassen

Ik sta op het punt 100.000 objecten in code te maken. Het zijn kleine, alleen met 2 of 3 eigenschappen. Ik zal ze in een generieke lijst plaatsen en als ze dat zijn, zal ik ze herhalen en waarde acontroleren en misschien waarde bbijwerken.

Is het sneller/beter om deze objecten als klasse of als struct te maken?

BEWERKEN

a. De eigenschappen zijn waardetypes (behalve de tekenreeks denk ik?)

b. Ze hebben misschien (we weten het nog niet zeker) een valideringsmethode

BEWERK 2

Ik vroeg me af: worden objecten op de hoop en de stapel gelijk verwerkt door de vuilnisman, of werkt dat anders?


Antwoord 1, autoriteit 100%

Is het snellerom deze objecten als klasse of als struct te maken?

U bent de enige persoon die het antwoord op die vraag kan bepalen. Probeer het op beide manieren: meeteen zinvolle, gebruikersgerichte, relevante prestatiestatistiek, en dan weet u of de wijziging een betekenisvol effect heeft op echte gebruikers in relevante scenario’s.

Structs verbruiken minder heap-geheugen (omdat ze kleinerzijn en gemakkelijker kunnen worden gecomprimeerd, niet omdat ze “op de stapel” staan). Maar het duurt langer om ze te kopiëren dan een referentiekopie. Ik weet niet wat uw prestatiestatistieken zijn voor geheugengebruik of snelheid; er is hier een afweging en jij bent de persoon die weet wat het is.

Is het beterom deze objecten als klasse of als struct te maken?

Misschien klasse, misschien struct. Als vuistregel:
Als het object :
is
1. Klein
2. Logisch een onveranderlijke waarde
3. Er zijn er veel
Dan zou ik overwegen om er een structuur van te maken. Anders zou ik het bij een referentietype houden.

Als je een veld van een struct moet muteren, is het meestal beter om een ​​constructor te bouwen die een geheel nieuwe struct teruggeeft met het veld correct ingesteld. Dat is misschien iets langzamer (meet het!) maar logischerwijs veel gemakkelijker om over te redeneren.

Worden objecten op de hoop en de stapel gelijkelijk verwerkt door de vuilnisman?

Nee, ze zijn niet hetzelfde omdat objecten op de stapel de basis zijn van de verzameling. De vuilnisman hoeft nooit te vragen “Is dit ding op de stapel levend?” want het antwoord op die vraag is altijd “Ja, het ligt op de stapel”. (Je kunt er nu niet op vertrouwen om een ​​object in leven te houdenomdat de stapel een implementatiedetail is. De jitter mag optimalisaties introduceren die bijvoorbeeld registreren wat normaal een stapelwaarde zou zijn, en dan ligt het nooit op de stapel, zodat de WG niet weet dat het nog in leven is. Een geregistreerd object kan zijn nakomelingen agressief laten verzamelen, zodra het register dat eraan vasthoudt niet meer wordt gelezen.)

Maar de vuilnisman moetobjecten op de stapel als levend behandelen, op dezelfde manier als elk object waarvan bekend is dat het levend is, als levend. Het object op de stapel kan verwijzen naar aan heap toegewezen objecten die in leven moeten worden gehouden, dus de GC moet stapelobjecten behandelen als levende heap-toegewezen objecten om de live set te bepalen. Maar het is duidelijk dat ze nietworden behandeld als “levende objecten” om de hoop te comprimeren, omdat ze in de eerste plaats niet op de hoop staan.

Is dat duidelijk?


Antwoord 2, autoriteit 16%

Soms hoef je met structde constructor new() niet aan te roepen en de velden direct toe te wijzen, waardoor het veel sneller gaat dan normaal.

Voorbeeld:

Value[] list = new Value[N];
for (int i = 0; i < N; i++)
{
    list[i].id = i;
    list[i].isValid = true;
}

is ongeveer 2 tot 3 keer sneller dan

Value[] list = new Value[N];
for (int i = 0; i < N; i++)
{
    list[i] = new Value(i, true);
}

waarbij Valueeen structis met twee velden (iden isValid).

struct Value
{
    int id;
    bool isValid;
    public Value(int i, bool isValid)
    {
        this.i = i;
        this.isValid = isValid;
    }
}

Aan de andere kant zijn de items die moeten worden verplaatst of geselecteerde waardetypes al dat kopiëren je vertragen. Om het exacte antwoord te krijgen, vermoed ik dat je je code moet profileren en testen.


Antwoord 3, autoriteit 4%

Structuren lijken misschien op klassen, maar er zijn belangrijke verschillen waarvan u op de hoogte moet zijn. Allereerst zijn klassen referentietypen en structs waardetypen. Door structs te gebruiken, kunt u objecten maken die zich gedragen als de ingebouwde typen en ook profiteren van hun voordelen.

Als je de nieuwe operator belt voor een klasse, wordt deze op de heap toegewezen. Wanneer u echter een structiseert, wordt deze op de stapel gemaakt. Dit levert prestatiewinst op. Ook zul je niet te maken hebben met verwijzingen naar een instantie van een structeur zoals je zou doen met klassen. Je werkt rechtstreeks met de instantie struct. Daarom wordt bij het doorgeven van een struct aan een methode deze door waarde doorgegeven in plaats van als referentie.

Meer hier:

http://msdn.microsoft.com/en -us/library/aa288471(VS.71).aspx


Antwoord 4, autoriteit 4%

Arrays van structs worden op de heap weergegeven in een aaneengesloten geheugenblok, terwijl een array van objecten wordt weergegeven als een aaneengesloten referentieblok met de werkelijke objecten zelf elders op de heap, waardoor geheugen nodig is voor zowel de objecten als voor hun array-referenties.

In dit geval, aangezien u ze in een List<>plaatst (en een List<>wordt als back-up op een array geplaatst), zou het efficiënter zijn , qua geheugen om structs te gebruiken.

(Houd er echter rekening mee dat grote arrays hun weg zullen vinden op de Large Object Heap waar, als hun levensduur lang is, een negatief effect kan hebben op het geheugenbeheer van uw proces. Onthoud ook dat geheugen niet de enige overweging is. )


Antwoord 5, autoriteit 3%

Als ze waardesemantiek hebben, moet je waarschijnlijk een struct gebruiken. Als ze referentiesemantiek hebben, moet je waarschijnlijk een klasse gebruiken. Er zijn uitzonderingen, die meestal neigen naar het maken van een klasse, zelfs als er waardesemantiek is, maar begin daar.

Wat betreft je tweede bewerking, de GC behandelt alleen de hoop, maar er is veel meer hoopruimte dan stapelruimte, dus dingen op de stapel plaatsen is niet altijd een overwinning. Daarnaast zal er hoe dan ook een lijst met struct-types en een lijst met class-types op de stapel staan, dus dit is in dit geval niet relevant.

Bewerken:

Ik begin de term kwaadals schadelijk te beschouwen. Het is tenslotte een slecht idee om een ​​klasse veranderlijk te maken als het niet actief nodig is, en ik zou niet uitsluiten dat ik ooit een veranderlijke structuur zal gebruiken. Het is echter zo vaak een slecht idee dat het bijna altijd een slecht idee is, maar meestal valt het gewoon niet samen met de waardesemantiek, dus het heeft geen zin om in het gegeven geval een struct te gebruiken.

Er kunnen redelijke uitzonderingen zijn met privé geneste structs, waarbij al het gebruik van die struct dus beperkt is tot een zeer beperkte reikwijdte. Dit is hier echter niet van toepassing.

Echt, ik denk dat “het muteert, dus het is een slechte stuct” niet veel beter is dan doorgaan over de heap en de stapel (wat op zijn minst enige invloed heeft op de prestaties, zelfs als deze vaak verkeerd wordt voorgesteld). “Het muteert, dus het heeft vrijwaarschijnlijkgeen zin om het te beschouwen als waarde-semantiek, dus het is een slechte structuur” is slechts een klein beetje anders, maar belangrijker vind ik.


Antwoord 6, autoriteit 2%

De beste oplossing is om te meten, opnieuw te meten en dan nog eens te meten. Er kunnen details zijn over wat u doet waardoor een vereenvoudigd, eenvoudig antwoord, zoals ‘gebruik structs’ of ‘gebruik klassen’, moeilijk kan zijn.


Antwoord 7, autoriteit 2%

Een struct is in wezen niets meer of minder dan een verzameling velden. In .NET is het mogelijk dat een structuur “doet alsof” een object is, en voor elk structuurtype definieert .NET impliciet een heap-objecttype met dezelfde velden en methoden die – als een heap-object – zich als een object zullen gedragen . Een variabele die een verwijzing naar zo’n heap-object (“boxed”-structuur) bevat, zal referentiesemantiek vertonen, maar een variabele die rechtstreeks een struct bevat, is gewoon een aggregatie van variabelen.

Ik denk dat veel van de struct-versus-class verwarring voortkomt uit het feit dat structuren twee heel verschillende gebruiksgevallen hebben, die heel verschillende ontwerprichtlijnen zouden moeten hebben, maar de MS-richtlijnen maken geen onderscheid tussen hen. Soms is er behoefte aan iets dat zich als een object gedraagt; in dat geval zijn de MS-richtlijnen redelijk, hoewel de “limiet van 16 bytes” waarschijnlijk meer als 24-32 zou moeten zijn. Soms is echter een aggregatie van variabelen nodig. Een struct die voor dat doel wordt gebruikt, moet eenvoudigweg bestaan ​​uit een aantal openbare velden en mogelijk een Equals-override, ToString-override en IEquatable(itsType).Equalsimplementatie. Structuren die worden gebruikt als aggregaties van velden zijn geen objecten en zouden dat ook niet moeten doen. Vanuit het oogpunt van de structuur zou de betekenis van veld niets meer of minder moeten zijn dan “het laatste dat naar dit veld wordt geschreven”. Elke aanvullende betekenis moet worden bepaald door de klantcode.

Als een variabele-aggregerende struct bijvoorbeeld leden Minimumen Maximumheeft, mag de struct zelf niet beloven dat Minimum <= Maximum. Code die een dergelijke structuur als parameter ontvangt, moet zich gedragen alsof er aparte Minimumen Maximumwaarden aan zijn doorgegeven. Een vereiste dat Minimumniet groter mag zijn dan Maximummoet worden beschouwd als een vereiste dat een Minimum-parameter niet groter mag zijn dan een afzonderlijk doorgegeven Maximuméén.

Een handig patroon om soms te overwegen is om een ​​klasse ExposedHolder<T>te definiëren als:

class ExposedHolder<T>
{
  public T Value;
  ExposedHolder() { }
  ExposedHolder(T val) { Value = T; }
}

Als men een List<ExposedHolder<someStruct>>heeft, waarbij someStructeen variabele-aggregerende struct is, kan men dingen doen als myList[3].Value.someField += 7;, maar als u myList[3].Valueaan andere code geeft, krijgt deze de inhoud van Valuein plaats van deze te geven een middel om het te veranderen. Als men daarentegen een List<someStruct>gebruikt, zou het nodig zijn om var temp=myList[3]; temp.someField += 7; myList[3] = temp;. Als men een veranderlijk klassetype zou gebruiken, zou het voor het blootstellen van de inhoud van myList[3]aan externe code nodig zijn om alle velden naar een ander object te kopiëren. Als men een onveranderlijk klassetype of een “object-stijl” struct zou gebruiken, zou het nodig zijn om een ​​nieuwe instantie te construeren die eruitzag als myList[3]behalve someFielddie anders was, en sla die nieuwe instantie vervolgens op in de lijst.

Een aanvullende opmerking: als je een groot aantal vergelijkbare dingen opslaat, kan het goed zijn om ze op te slaan in mogelijk geneste arrays van structuren, waarbij je bij voorkeur probeert de grootte van elke array tussen 1K en 64K of zo te houden. Arrays van structuren zijn speciaal, omdat indexeren een directe verwijzing oplevert naar een structuur binnenin, dus men kan zeggen “a[12].x = 5;”. Hoewel men array-achtige objecten kan definiëren, staat C# niet toe dat ze dergelijke syntaxis delen met arrays.


Antwoord 8

Gebruik klassen.

Algemeen. Waarom zou u waarde b niet bijwerken terwijl u ze maakt?


Antwoord 9

Vanuit een c++-perspectief ben ik het ermee eens dat het langzamer zal zijn om de eigenschappen van een struct te wijzigen in vergelijking met een klasse. Maar ik denk wel dat ze sneller kunnen worden gelezen omdat de structuur op de stapel wordt toegewezen in plaats van op de heap. Het lezen van gegevens van de heap vereist meer controles dan van de stapel.


Antwoord 10

Wel, als u met structures gaat, dan kunt u van de reeks kwijt en gebruikt u vaste maat of byte buffer.

Dat is RE: PRESTATIE.

Other episodes