Wat is het verschil tussen struct en class in .NET?

Wat is het verschil tussen struct en class in .NET?


Antwoord 1, autoriteit 100%

In .NET zijn er twee categorieën typen, referentietypen en waardetypen.

Structs zijn waardetypes en klassen zijn referentietypes.

Het algemene verschil is dat een referentietype op de heap leeft, en een waardetype inline, dat wil zeggen, waar uw variabele of veld ook is gedefinieerd.

Een variabele die een waardetype bevat, bevat de volledige waardetype waarde. Voor een struct betekent dat dat de variabele de hele struct bevat, met al zijn velden.

Een variabele die een verwijzingstype bevat, bevat een aanwijzer, of een verwijzing naar een andere plaats in het geheugen waar de werkelijke waarde zich bevindt.

Dit heeft om te beginnen één voordeel:

  • waardetypes bevatten altijd een waarde
  • verwijzingstypen kunnen een null-verwijzing bevatten, wat betekent dat ze op dit moment helemaal nergens naar verwijzen

Intern worden referentietypes geïmplementeerd als aanwijzingen, en wetende dat, en wetende hoe variabele toewijzing werkt, zijn er andere gedragspatronen:

  • kopiëren van de inhoud van een type waardetype variabele naar een andere variabele, kopieert de volledige inhoud naar de nieuwe variabele, waardoor de twee onderscheiden worden. Met andere woorden, na het kopiëren hebben wijzigingen aan de ene geen invloed op de andere
  • kopiëren van de inhoud van een referentietype variabele naar een andere variabele, kopieert de referentie, wat betekent dat je nu twee referenties hebt naar dezelfde ergens anders opslag van de feitelijke gegevens . Met andere woorden, na het kopiëren lijkt het veranderen van de gegevens in de ene referentie ook de andere te beïnvloeden, maar alleen omdat je eigenlijk op beide plaatsen naar dezelfde gegevens kijkt

Als je variabelen of velden declareert, dan verschillen de twee typen als volgt:

  • variabele: waardetype leeft op de stapel, referentietype leeft op de stapel als een verwijzing naar ergens in het heapgeheugen waar het werkelijke geheugen leeft (hoewel opmerking Eric Lipperts serie artikelen : De stapel is een implementatiedetail.)
  • class/struct-field: value type leeft volledig binnen het type, referentietype leeft binnen het type als een verwijzing naar ergens in het heapgeheugen waar het daadwerkelijke geheugen leeft .

Antwoord 2, autoriteit 20%

Een korte samenvatting van elk:

Alleen lessen:

  • Kan overerving ondersteunen
  • Zijn referentie (aanwijzer) typen
  • De referentie kan nul zijn
  • Heeft geheugenoverhead per nieuwe instantie

Alleen structuren:

  • Kan overname niet ondersteunen
  • Zijn waardetypen
  • Worden doorgegeven door waarde (zoals gehele getallen)
  • Kan geen null-referentie hebben (tenzij Nullable wordt gebruikt)
  • Heeft geen geheugenoverhead per nieuwe instantie – tenzij ‘boxed’

Zowel klassen als structuren:

  • Worden samengestelde gegevenstypen doorgaans gebruikt om een ​​paar variabelen te bevatten die een logische relatie hebben
  • Kan methoden en gebeurtenissen bevatten
  • Kan interfaces ondersteunen

Antwoord 3, autoriteit 4%

In .NET maken de struct- en klassedeclaraties onderscheid tussen referentietypes en waardetypes.

Wanneer u een referentietype doorgeeft, is er slechts één daadwerkelijk opgeslagen. Alle code die toegang heeft tot de instantie, heeft toegang tot dezelfde.

Als je een waardetype doorgeeft, is elk een kopie. Alle code werkt op zijn eigen kopie.

Dit kan met een voorbeeld worden getoond:

struct MyStruct 
{
    string MyProperty { get; set; }
}
void ChangeMyStruct(MyStruct input) 
{ 
   input.MyProperty = "new value";
}
...
// Create value type
MyStruct testStruct = new MyStruct { MyProperty = "initial value" }; 
ChangeMyStruct(testStruct);
// Value of testStruct.MyProperty is still "initial value"
// - the method changed a new copy of the structure.

Voor een klas zou dit anders zijn

class MyClass 
{
    string MyProperty { get; set; }
}
void ChangeMyClass(MyClass input) 
{ 
   input.MyProperty = "new value";
}
...
// Create reference type
MyClass testClass = new MyClass { MyProperty = "initial value" };
ChangeMyClass(testClass);
// Value of testClass.MyProperty is now "new value" 
// - the method changed the instance passed.

Klassen kunnen niets zijn – de verwijzing kan naar een null verwijzen.

Structs zijn de werkelijke waarde – ze kunnen leeg zijn, maar nooit null. Om deze reden hebben structs altijd een standaardconstructor zonder parameters – ze hebben een ‘startwaarde’ nodig.


Antwoord 4, autoriteit 4%

Verschil tussen structuren en klassen:

  • Structuren zijn van het waardetype terwijl Klassen van het referentietype zijn.
  • Structs worden op de stapel opgeslagen terwijl Klassen worden opgeslagen op de
    hoop
    .
  • Waardetypen behouden hun waarde in het geheugen waar ze worden gedeclareerd, maar
    referentietype bevat een verwijzing naar een objectgeheugen.
  • Waardetypen worden onmiddellijk vernietigd nadat het bereik verloren is gegaan, terwijl
    referentietype alleen de variabele vernietigt nadat het bereik verloren is gegaan. De
    object wordt later vernietigd door de vuilnisman.
  • Als je struct naar een andere struct kopieert, een nieuwe kopie van die struct
    wordt gemaakt gewijzigd van één structuur heeft geen invloed op de waarde van de
    andere structuur.
  • Als je een klas naar een andere klas kopieert, wordt alleen de
    referentievariabele.
  • Beide referentievariabelen wijzen naar hetzelfde object op de heap.
    Wijziging naar één variabele heeft invloed op de andere referentievariabele.
  • Structs kunnen geen destructors hebben, maar klassen kunnen destructors hebben.
  • Structs kunnen geen expliciete parameterloze constructors hebben, terwijl klassen dat wel kunnen. Structuren ondersteunen geen overerving, maar klassen wel. Beide
    ondersteuning van overerving vanuit een interface.
  • De constructies zijn van het verzegelde type.

Antwoord 5, autoriteit 2%

Van Microsoft’s Kiezen tussen klasse en structuur

Als vuistregel geldt dat de meeste typen in een framework moeten zijn
klassen. Er zijn echter enkele situaties waarin de
kenmerken van een waardetype maken het geschikter om te gebruiken
structuren.

? OVERweeg een struct in plaats van een klasse:

  • Als instanties van het type klein zijn en vaak van korte duur zijn of vaak zijn ingebed in andere objecten.

X VERMIJD een struct tenzij het type alle van het volgende heeft
kenmerken:

  • Het vertegenwoordigt logischerwijs een enkele waarde, vergelijkbaar met primitieve typen (int, double, etc.).
  • Het heeft een instantiegrootte van minder dan 16 bytes.
  • Het is onveranderlijk. (kan niet worden gewijzigd)
  • Het hoeft niet vaak in dozen te worden gedaan.

Antwoord 6, autoriteit 2%

Naast alle verschillen beschreven in de andere antwoorden:

  1. Structs kunnen geen expliciete parameterloze constructor hebben terwijl een klasse dat wel kan
  2. Structs kunnen geen destructors hebben, terwijl een klasse dat wel kan
  3. Structs kunnen niet overerven van een andere struct of klasse, terwijl een klasse kan erven van een andere klasse. (Zowel structs als klassen kunnen vanuit een interface worden geïmplementeerd.)

Als je op zoek bent naar een video waarin alle verschillen worden uitgelegd, kun je Deel 29 – C#-zelfstudie – Verschil tussen klassen en structs in C#.


Antwoord 7

Instances van klassen worden opgeslagen op de beheerde heap. Alle variabelen die een instantie ‘bevatten’, zijn gewoon een verwijzing naar de instantie op de heap. Het doorgeven van een object aan een methode resulteert in het doorgeven van een kopie van de referentie, niet het object zelf.

Structuren (technisch gezien, waardetypen) worden opgeslagen waar ze ook worden gebruikt, net als een primitief type. De inhoud kan op elk moment door de runtime worden gekopieerd en zonder een beroep te doen op een aangepaste copy-constructor. Het doorgeven van een waardetype aan een methode houdt in dat de volledige waarde wordt gekopieerd, opnieuw zonder een aanpasbare code aan te roepen.

Het onderscheid wordt beter gemaakt door de C++/CLI-namen: “ref class” is een klasse zoals eerst beschreven, “value class” is een klasse zoals als tweede beschreven. De trefwoorden ‘class’ en ‘struct’ zoals gebruikt door C# zijn gewoon iets dat moet worden geleerd.


Antwoord 8

Ik visualisaties, en hier heb ik er een gemaakt om de basisverschillen tussen structs en klassen.
voer hier de afbeeldingsbeschrijving in


Kijk hieronder voor meer informatie:


Antwoord 9

Om aan de andere antwoorden toe te voegen, is er één fundamenteel verschil dat het vermelden waard is, en dat is hoe de gegevens in arrays worden opgeslagen, omdat dit een groot effect kan hebben op de prestaties.

  • Bij een struct bevat de array de instantie van de struct
  • Bij een klasse bevat de array een verwijzing naar een instantie van de klasse elders in het geheugen

Dus een array van structs ziet er zo uit in het geheugen

[struct][struct][struct][struct][struct][struct][struct][struct]

Terwijl een reeks klassen er zo uitziet

[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]

Bij een array van klassen worden de waarden waarin je geïnteresseerd bent niet in de array opgeslagen, maar ergens anders in het geheugen.

Voor een overgrote meerderheid van applicaties maakt dit verschil niet echt uit, maar in high-performance code zal dit de plaats van gegevens in het geheugen beïnvloeden en een grote impact hebben op de prestaties van de CPU-cache. Het gebruik van klassen wanneer je structs had kunnen/moeten gebruiken, zal het aantal cache-missers op de CPU enorm vergroten.

Het langzaamste wat een moderne CPU doet, is niet het verwerken van cijfers, maar het ophalen van gegevens uit het geheugen, en een L1-cachehit is vele malen sneller dan het lezen van gegevens uit RAM.

Hier is wat code die u kunt testen. Op mijn machine duurt het doorlopen van de class-array ~3x langer dan de struct-array.

    private struct PerformanceStruct
    {
        public int i1;
        public int i2;
    }
    private class PerformanceClass
    {
        public int i1;
        public int i2;
    }
    private static void DoTest()
    {
        var structArray = new PerformanceStruct[100000000];
        var classArray = new PerformanceClass[structArray.Length];
        for (var i = 0; i < structArray.Length; i++)
        {
            structArray[i] = new PerformanceStruct();
            classArray[i] = new PerformanceClass();
        }
        long total = 0;
        var sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < structArray.Length; i++)
        {
            total += structArray[i].i1 + structArray[i].i2;
        }
        sw.Stop();
        Console.WriteLine($"Struct Time: {sw.ElapsedMilliseconds}");
        sw = new Stopwatch();
        sw.Start();
        for (var loops = 0; loops < 100; loops++)
        for (var i = 0; i < classArray.Length; i++)
        {
            total += classArray[i].i1 + classArray[i].i2;
        }
        Console.WriteLine($"Class Time: {sw.ElapsedMilliseconds}");
    }

Antwoord 10

Structeren Klasse
Type Waardetype Referentietype
Waar Op stapel / Inline in type bevattend Op Heap
Deallocatie Stapel wordt afgewikkeld / type met inhoud wordt opgeheven Vuilnis ingezameld
Arrays Inline zijn elementen de feitelijke instanties van het waardetype Buiten de lijn, elementen zijn slechts verwijzingen naar instanties van het referentietype die zich op de heap bevinden
Al-Del-kosten Goedkope toewijzing-deallocation Dure toewijzing-deallocation
Geheugengebruik Boxed bij cast naar een referentietype of een van de interfaces die ze implementeren,
Unboxed bij cast terug naar waardetype
(Negatieve impact omdat boxen objecten zijn die op de heap worden toegewezen en worden verzameld)
Geen boksen-uitpakken
Opdrachten Gehele gegevens kopiëren Kopieer de referentie
Wijzigen in een instantie Heeft geen invloed op de kopieën Beïnvloed alle verwijzingen die naar de instantie verwijzen
Veranderbaarheid Moet onveranderlijk zijn Veranderbaar
Bevolking In sommige situaties De meeste typen in een raamwerk moeten klassen zijn
Levensduur Van korte duur Lange levensduur
Vernietiger Kan niet hebben Kan hebben
Overerving Alleen vanuit een interface Volledige ondersteuning
Polymorfisme Nee Ja
Verzegeld Ja Wanneer hebben sealed zoekwoord (C#), of sealed attribuut (F#)
Constructeur Kan geen expliciete parameterloze constructors hebben Elke constructor
Null-toewijzingen Wanneer gemarkeerd met een nullable vraagteken Ja (indien gemarkeerd met een nullable vraagteken in C# 8+ en F# 5+ 1)
Abstract Nee Wanneer hebben abstract trefwoord (C#), of AbstractClass attribuut (F#)
Modifiers voor ledentoegang public, private, internal public, protected, internal, protected internal, private protected< /td>

Antwoord 11

Structuur versus klasse

Een structuur is een waardetype en wordt dus op de stapel opgeslagen, maar een klasse is een referentietype en wordt op de heap opgeslagen.

Een structuur ondersteunt geen overerving en polymorfisme, maar een klasse ondersteunt beide.

Standaard zijn alle structleden openbaar, maar klasleden zijn standaard privé van aard.

Omdat een structuur een waardetype is, kunnen we geen null toewijzen aan een struct-object, maar dat is niet het geval voor een klasse.


Antwoord 12

Nou, om te beginnen wordt een struct doorgegeven op basis van waarde in plaats van op referentie. Structuren zijn goed voor relatief eenvoudige datastructuren, terwijl klassen vanuit architectonisch oogpunt veel meer flexibiliteit hebben via polymorfisme en overerving.

Anderen kunnen je waarschijnlijk meer details geven dan ik, maar ik gebruik structs wanneer de structuur waar ik voor ga eenvoudig is.


Antwoord 13

Om het compleet te maken, is er nog een ander verschil bij het gebruik van de Equals methode, die door alle klassen en structuren wordt overgenomen.

Stel dat we een klasse en een structuur hebben:

class A{
  public int a, b;
}
struct B{
  public int a, b;
}

en in de Main-methode hebben we 4 objecten.

static void Main{
  A c1 = new A(), c2 = new A();
  c1.a = c1.b = c2.a = c2.b = 1;
  B s1 = new B(), s2 = new B();
  s1.a = s1.b = s2.a = s2.b = 1;
}

Dan:

s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false

Dus zijn structuren geschikt voor numerieke objecten, zoals punten (behalve x- en y-coördinaten). En lessen zijn geschikt voor anderen. Zelfs als 2 personen dezelfde naam, lengte, gewicht… hebben, zijn het nog steeds 2 personen.


Antwoord 14

  1. Gebeurtenissen die in een klasse zijn gedeclareerd, hebben hun += en -= toegang automatisch vergrendeld via een slot (this) om ze thread-safe te maken (statische gebeurtenissen zijn vergrendeld op het type van de klasse). Gebeurtenissen die in een struct worden gedeclareerd, hebben hun += en -= toegang niet automatisch vergrendeld. Een lock (this) voor een struct zou niet werken, omdat je alleen een expressie van het referentietype kunt vergrendelen.

  2. Het maken van een instantie van het type struct kan geen garbagecollection veroorzaken (tenzij de constructor direct of indirect een instantie van het referentietype maakt), terwijl het maken van een instantie van het referentietype tot een garbagecollection kan leiden.

  3. Een struct heeft altijd een ingebouwde openbare standaardconstructor.

    class DefaultConstructor
    {
        static void Eg()
        {
            Direct     yes = new   Direct(); // Always compiles OK
            InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
            //...
        }
    }
    

    Dit betekent dat een struct altijd instantieerbaar is, terwijl een klasse dat misschien niet is, omdat alle constructors privé kunnen zijn.

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
    
  4. Een struct kan geen destructor hebben. Een destructor is slechts een overschrijving van het object. Voltooi in vermomming, en structs, die waardetypes zijn, zijn niet onderhevig aan garbagecollection.

    struct Direct
    {
        ~Direct() {} // Compile-time error
    }
    class InDirect
    {
        ~InDirect() {} // Compiles OK
    }
    And the CIL for ~Indirect() looks like this:
    .method family hidebysig virtual instance void
            Finalize() cil managed
    {
      // ...
    } // end of method Indirect::Finalize
    
  5. Een struct is impliciet verzegeld, een klasse niet.
    Een struct kan niet abstract zijn, een klasse wel.
    Een struct kan : base() niet aanroepen in zijn constructor, terwijl een klasse zonder expliciete basisklasse dat wel kan.
    Een struct kan een andere klasse niet uitbreiden, een klasse wel.
    Een struct kan geen beschermde leden declareren (bijvoorbeeld velden, geneste typen) die een klasse wel kan.
    Een struct kan geen abstracte functieleden declareren, een abstracte klasse wel.
    Een struct kan geen virtuele functieleden declareren, een klasse wel.
    Een struct kan geen verzegelde functieleden declareren, een klasse wel.
    Een struct kan geen override-functieleden declareren, een klasse wel.
    De enige uitzondering op deze regel is dat een struct de virtuele methoden van System.Object, namelijk, Equals() en GetHashCode() en ToString() kan overschrijven.


Antwoord 15

Zoals eerder vermeld: klassen zijn referentietypes, terwijl structs waardetypes zijn met alle gevolgen van dien.

Als vuistregel raden Framework Design Guidelines aan om Structs te gebruiken in plaats van klassen als:

  • Het heeft een instantiegrootte van minder dan 16 bytes
  • Het vertegenwoordigt logischerwijs een enkele waarde, vergelijkbaar met primitieve typen (int, double, etc.)
  • Het is onveranderlijk
  • Het hoeft niet vaak in dozen te worden gedaan

Antwoord 16

Naast het fundamentele verschil van toegangsspecificatie, en enkele hierboven genoemde, zou ik enkele van de belangrijkste verschillen willen toevoegen, waaronder enkele van de hierboven genoemde, met een codevoorbeeld met uitvoer, dat een duidelijker beeld geeft van de referentie en waarde

Structuren:

  • Zijn waardetypen en vereisen geen heaptoewijzing.
  • Geheugentoewijzing is anders en wordt in de stapel opgeslagen
  • Nuttig voor kleine gegevensstructuren
  • Beïnvloed de prestaties, wanneer we waarde doorgeven aan methode, geven we de volledige gegevensstructuur door en wordt alles doorgegeven aan de stapel.
  • Constructor retourneert eenvoudig de struct-waarde zelf (meestal op een tijdelijke locatie op de stapel), en deze waarde wordt vervolgens indien nodig gekopieerd
  • De variabelen hebben elk hun eigen kopie van de gegevens en het is niet mogelijk dat bewerkingen op de ene van invloed zijn op de andere.
  • Ondersteunen geen door de gebruiker gespecificeerde overerving, en ze erven impliciet van type object

Klasse:

  • Waarde voor referentietype
  • Opgeslagen in hoop
  • Een verwijzing naar een dynamisch toegewezen object opslaan
  • Constructeurs worden aangeroepen met de nieuwe operator, maar dat wijst geen geheugen toe aan de heap
  • Meerdere variabelen kunnen een verwijzing naar hetzelfde object hebben
  • Het is mogelijk dat bewerkingen op de ene variabele invloed hebben op het object waarnaar wordt verwezen door de andere variabele

Codevoorbeeld

    static void Main(string[] args)
    {
        //Struct
        myStruct objStruct = new myStruct();
        objStruct.x = 10;
        Console.WriteLine("Initial value of Struct Object is: " + objStruct.x);
        Console.WriteLine();
        methodStruct(objStruct);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Struct Object is: " + objStruct.x);
        Console.WriteLine();
        //Class
        myClass objClass = new myClass(10);
        Console.WriteLine("Initial value of Class Object is: " + objClass.x);
        Console.WriteLine();
        methodClass(objClass);
        Console.WriteLine();
        Console.WriteLine("After Method call value of Class Object is: " + objClass.x);
        Console.Read();
    }
    static void methodStruct(myStruct newStruct)
    {
        newStruct.x = 20;
        Console.WriteLine("Inside Struct Method");
        Console.WriteLine("Inside Method value of Struct Object is: " + newStruct.x);
    }
    static void methodClass(myClass newClass)
    {
        newClass.x = 20;
        Console.WriteLine("Inside Class Method");
        Console.WriteLine("Inside Method value of Class Object is: " + newClass.x);
    }
    public struct myStruct
    {
        public int x;
        public myStruct(int xCons)
        {
            this.x = xCons;
        }
    }
    public class myClass
    {
        public int x;
        public myClass(int xCons)
        {
            this.x = xCons;
        }
    }

Uitvoer

Initiële waarde van Struct Object is: 10

Inside Struct-methode
Inside Method-waarde van Struct Object is: 20

Na methode-aanroepwaarde van Struct Object is: 10

Initiële waarde van Class Object is: 10

Inside Class-methode
Inside Method-waarde van Class Object is: 20

Na methode-aanroepwaarde van klasseobject is: 20

Hier kun je duidelijk het verschil zien tussen call by value en call by reference.


Antwoord 17

Er is een interessant geval van “class vs struct”-puzzel – een situatie waarin je verschillende resultaten van de methode moet retourneren: kies welke je wilt gebruiken. Als je het ValueTuple-verhaal kent, weet je dat ValueTuple (struct) is toegevoegd omdat het effectiever zou moeten zijn dan Tuple (class). Maar wat betekent het in cijfers? Twee tests: de ene is struct/class die 2 velden heeft, de andere met struct/class die 8 velden heeft (met dimensie meer dan 4 – class zou effectiever moeten worden dan struct in termen van processorticks, maar natuurlijk moet ook GC-belasting worden overwogen ).

P.S. Een andere benchmark voor een specifiek geval ‘sturct of class with collections’ is er: https://stackoverflow.com/a/45276657/506147

BenchmarkDotNet=v0.10.10, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233540 Hz, Resolution=309.2586 ns, Timer=TSC
.NET Core SDK=2.0.3
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2115.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
            Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
------------------ |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
  TestStructReturn |  Clr |     Clr | 17.57 ns | 0.1960 ns | 0.1834 ns | 17.25 ns | 17.89 ns | 17.55 ns |    4 | 0.0127 |      40 B |
   TestClassReturn |  Clr |     Clr | 21.93 ns | 0.4554 ns | 0.5244 ns | 21.17 ns | 23.26 ns | 21.86 ns |    5 | 0.0229 |      72 B |
 TestStructReturn8 |  Clr |     Clr | 38.99 ns | 0.8302 ns | 1.4097 ns | 37.36 ns | 42.35 ns | 38.50 ns |    8 | 0.0127 |      40 B |
  TestClassReturn8 |  Clr |     Clr | 23.69 ns | 0.5373 ns | 0.6987 ns | 22.70 ns | 25.24 ns | 23.37 ns |    6 | 0.0305 |      96 B |
  TestStructReturn | Core |    Core | 12.28 ns | 0.1882 ns | 0.1760 ns | 11.92 ns | 12.57 ns | 12.30 ns |    1 | 0.0127 |      40 B |
   TestClassReturn | Core |    Core | 15.33 ns | 0.4343 ns | 0.4063 ns | 14.83 ns | 16.44 ns | 15.31 ns |    2 | 0.0229 |      72 B |
 TestStructReturn8 | Core |    Core | 34.11 ns | 0.7089 ns | 1.4954 ns | 31.52 ns | 36.81 ns | 34.03 ns |    7 | 0.0127 |      40 B |
  TestClassReturn8 | Core |    Core | 17.04 ns | 0.2299 ns | 0.2150 ns | 16.68 ns | 17.41 ns | 16.98 ns |    3 | 0.0305 |      96 B |

Codetest:

using System;
using System.Text;
using System.Collections.Generic;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Columns;
using BenchmarkDotNet.Attributes.Exporters;
using BenchmarkDotNet.Attributes.Jobs;
using DashboardCode.Routines.Json;
namespace Benchmark
{
    //[Config(typeof(MyManualConfig))]
    [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
    [ClrJob, CoreJob]
    [HtmlExporter, MarkdownExporter]
    [MemoryDiagnoser]
    public class BenchmarkStructOrClass
    {
        static TestStruct testStruct = new TestStruct();
        static TestClass testClass = new TestClass();
        static TestStruct8 testStruct8 = new TestStruct8();
        static TestClass8 testClass8 = new TestClass8();
        [Benchmark]
        public void TestStructReturn()
        {
            testStruct.TestMethod();
        }
        [Benchmark]
        public void TestClassReturn()
        {
            testClass.TestMethod();
        }
        [Benchmark]
        public void TestStructReturn8()
        {
            testStruct8.TestMethod();
        }
        [Benchmark]
        public void TestClassReturn8()
        {
            testClass8.TestMethod();
        }
        public class TestStruct
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance;
                public List<string> List;
            }
            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }
            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }
            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }
            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }
            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }
        public class TestClass
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance;
                public List<string> List;
            }
            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance;
            }
            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }
            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }
            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }
            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance = ++i;
                return x;
            }
        }
        public class TestStruct8
        {
            public int Number = 5;
            public struct StructType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }
            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }
            private StructType<int> Method1(int i)
            {
                return Method2(++i);
            }
            private StructType<int> Method2(int i)
            {
                return Method3(++i);
            }
            private StructType<int> Method3(int i)
            {
                return Method4(++i);
            }
            private StructType<int> Method4(int i)
            {
                var x = new StructType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }
        public class TestClass8
        {
            public int Number = 5;
            public class ClassType<T>
            {
                public T Instance1;
                public T Instance2;
                public T Instance3;
                public T Instance4;
                public T Instance5;
                public T Instance6;
                public T Instance7;
                public List<string> List;
            }
            public int TestMethod()
            {
                var s = Method1(1);
                return s.Instance1;
            }
            private ClassType<int> Method1(int i)
            {
                return Method2(++i);
            }
            private ClassType<int> Method2(int i)
            {
                return Method3(++i);
            }
            private ClassType<int> Method3(int i)
            {
                return Method4(++i);
            }
            private ClassType<int> Method4(int i)
            {
                var x = new ClassType<int>();
                x.List = new List<string>();
                x.Instance1 = ++i;
                return x;
            }
        }
    }
}

Antwoord 18

Structs zijn de werkelijke waarde – ze kunnen leeg zijn, maar nooit null

Dit is waar, maar houd er rekening mee dat vanaf .NET 2 structs een Nullable-versie ondersteunt en C# wat syntactische suiker levert om het gebruik gemakkelijker te maken.

int? value = null;
value  = 1;

Antwoord 19

Elke variabele of elk veld van een primitief waardetype of structuurtype bevat een unieke instantie van dat type, inclusief alle velden (openbaar en privé). Daarentegen kunnen variabelen of velden van referentietypes null bevatten, of kunnen ze verwijzen naar een object dat elders is opgeslagen en waarnaar ook een aantal andere referenties kan bestaan. De velden van een struct worden op dezelfde plaats opgeslagen als de variabele of het veld van dat structuurtype, die zich op de stapel kan bevinden of deel van een ander heap-object kan zijn.

Als u een variabele of veld van een primitief waardetype maakt, wordt deze gemaakt met een standaardwaarde; het creëren van een variabele of veld van een structuurtype zal een nieuwe instantie creëren, waarbij alle velden daarin op de standaardmanier worden gemaakt. Het maken van een nieuw exemplaar van een referentietype begint met het maken van alle velden daarin op de standaardmanier en voert vervolgens optionele aanvullende code uit, afhankelijk van het type.

Als u een variabele of veld van een primitief type naar een andere kopieert, wordt de waarde gekopieerd. Als u de ene variabele of het veld van het structuurtype naar een andere kopieert, worden alle velden (openbaar en privé) van de eerste instantie naar de laatste instantie gekopieerd. Als u de ene variabele of het veld van het referentietype naar een andere kopieert, verwijst de laatste naar dezelfde instantie als de eerste (indien aanwezig).

Het is belangrijk op te merken dat in sommige talen, zoals C++, het semantische gedrag van een type onafhankelijk is van hoe het is opgeslagen, maar dat geldt niet voor .NET. Als een type semantiek met veranderlijke waarden implementeert, kopieert het kopiëren van de ene variabele van dat type naar een andere de eigenschappen van de eerste naar een andere instantie, waarnaar wordt verwezen door de tweede, en het gebruik van een lid van de tweede om te muteren zal ervoor zorgen dat die tweede instantie wordt gewijzigd , maar niet de eerste. Als een type veranderlijke referentiesemantiek implementeert, zal het kopiëren van de ene variabele naar de andere en het gebruik van een lid van de tweede om het object te muteren, het object beïnvloeden waarnaar wordt verwezen door de eerste variabele; typen met onveranderlijke semantiek laten geen mutatie toe, dus het maakt semantisch niet uit of kopiëren een nieuwe instantie creëert of een andere verwijzing naar de eerste creëert.

In .NET is het mogelijk voor waardetypen om een ​​van de bovenstaande semantiek te implementeren, op voorwaarde dat al hun velden hetzelfde kunnen doen. Een referentietype kan echter alleen veranderlijke referentiesemantiek of onveranderlijke semantiek implementeren; waardetypes met velden van veranderlijke referentietypes zijn beperkt tot het implementeren van veranderlijke referentiesemantiek of vreemde hybride semantiek.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes