Réponses:
Dans .NET, il existe deux catégories de types, les types de référence et les types de valeur .
Les structures sont des types de valeur et les classes sont des types de référence .
La différence générale est qu'un type de référence vit sur le tas et qu'un type de valeur vit en ligne, c'est-à-dire, où que ce soit votre variable ou champ est défini.
Une variable contenant un type de valeur contient la valeur entière du type de valeur. Pour une structure, cela signifie que la variable contient la structure entière, avec tous ses champs.
Une variable contenant un type de référence contient un pointeur ou une référence à un autre endroit de la mémoire où réside la valeur réelle.
Cela présente un avantage, pour commencer:
En interne, les types de référence sont implémentés en tant que pointeurs, et sachant que, et sachant comment fonctionne l'affectation des variables, il existe d'autres modèles de comportement:
Lorsque vous déclarez des variables ou des champs, voici comment les deux types diffèrent:
Un bref résumé de chacun:
Cours uniquement:
Structures uniquement:
Classes et structures:
c# struct memory overhead
et j'ai trouvé cette réponse de Hans Passant qui dit que non, ce n'est pas le cas non plus. Alors qu'est - ce que tu veux dire?
class
sont de la mémoire gérée (gérée par le garbage collector), tandis que les instances de struct
ne le sont pas .
Dans .NET, les déclarations de structure et de classe différencient les types de référence et les types de valeur.
Lorsque vous passez un type de référence, un seul est réellement stocké. Tout le code qui accède à l'instance accède au même.
Lorsque vous passez un type de valeur, chacun est une copie. Tout le code fonctionne sur sa propre copie.
Cela peut être montré avec un exemple:
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.
Pour une classe, ce serait différent
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.
Les classes ne peuvent être rien - la référence peut pointer vers une valeur nulle.
Les structures sont la valeur réelle - elles peuvent être vides mais jamais nulles. Pour cette raison, les structures ont toujours un constructeur par défaut sans paramètres - elles ont besoin d'une «valeur de départ».
Différence entre Structs et Classes:
Du choix de Microsoft entre la classe et la structure ...
En règle générale, la majorité des types dans un framework doivent être des classes. Il existe cependant certaines situations dans lesquelles les caractéristiques d'un type de valeur rendent plus approprié l'utilisation de structures.
✓ CONSIDÉRER une structure au lieu d'une classe:
- Si les instances du type sont petites et généralement de courte durée ou sont généralement intégrées dans d'autres objets.
X ÉVITEZ une structure à moins que le type n'ait toutes les caractéristiques suivantes:
- Il représente logiquement une valeur unique, similaire aux types primitifs (int, double, etc.).
- Il a une taille d'instance inférieure à 16 octets.
- C'est immuable. (ne peut pas être modifié)
- Il n'aura pas à être emballé fréquemment.
En plus de toutes les différences décrites dans les autres réponses:
Si vous recherchez une vidéo expliquant toutes les différences, vous pouvez consulter la Partie 29 - Tutoriel C # - Différence entre les classes et les structures en C # .
Les instances de classes sont stockées sur le tas géré. Toutes les variables «contenant» une instance sont simplement une référence à l'instance sur le tas. Passer un objet à une méthode entraîne la transmission d'une copie de la référence, et non l'objet lui-même.
Les structures (techniquement, les types de valeur) sont stockées partout où elles sont utilisées, un peu comme un type primitif. Le contenu peut être copié par le runtime à tout moment et sans invoquer un constructeur de copie personnalisé. La transmission d'un type de valeur à une méthode implique la copie de la valeur entière, encore une fois sans invoquer de code personnalisable.
La distinction est améliorée par les noms C ++ / CLI: "ref class" est une classe telle que décrite en premier, "value class" est une classe telle que décrite en second. Les mots-clés "classe" et "struct" tels qu'utilisés par C # sont simplement quelque chose qui doit être appris.
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| | Struct | Class |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
| Type | Value-type | Reference-type |
| Where | On stack / Inline in containing type | On Heap |
| Deallocation | Stack unwinds / containing type gets deallocated | Garbage Collected |
| Arrays | Inline, elements are the actual instances of the value type | Out of line, elements are just references to instances of the reference type residing on the heap |
| Aldel Cost | Cheap allocation-deallocation | Expensive allocation-deallocation |
| Memory usage | Boxed when cast to a reference type or one of the interfaces they implement, | No boxing-unboxing |
| | Unboxed when cast back to value type | |
| | (Negative impact because boxes are objects that are allocated on the heap and are garbage-collected) | |
| Assignments | Copy entire data | Copy the reference |
| Change to an instance | Does not affect any of its copies | Affect all references pointing to the instance |
| Mutability | Should be immutable | Mutable |
| Population | In some situations | Majority of types in a framework should be classes |
| Lifetime | Short-lived | Long-lived |
| Destructor | Cannot have | Can have |
| Inheritance | Only from an interface | Full support |
| Polymorphism | No | Yes |
| Sealed | Yes | When have sealed keyword |
| Constructor | Can not have explicit parameterless constructors | Any constructor |
| Null-assignments | When marked with nullable question mark | Yes (+ When marked with nullable question mark in C# 8+) |
| Abstract | No | When have abstract keyword |
| Member Access Modifiers| public, private, internal | public, protected, internal, protected internal, private protected |
+------------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
Structure vs classe
Une structure est un type de valeur, elle est donc stockée sur la pile, mais une classe est un type de référence et est stockée sur le tas.
Une structure ne prend pas en charge l'héritage et le polymorphisme, mais une classe prend en charge les deux.
Par défaut, tous les membres de la structure sont publics, mais les membres de la classe sont par défaut de nature privée.
Comme une structure est un type de valeur, nous ne pouvons pas affecter null à un objet struct, mais ce n'est pas le cas pour une classe.
Pour ajouter aux autres réponses, il y a une différence fondamentale qui mérite d'être notée, et c'est la façon dont les données sont stockées dans les tableaux car cela peut avoir un effet majeur sur les performances.
Donc, un tableau de structures ressemble à ceci en mémoire
[struct][struct][struct][struct][struct][struct][struct][struct]
Alors qu'un tableau de classes ressemble à ceci
[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]
Avec un tableau de classes, les valeurs qui vous intéressent ne sont pas stockées dans le tableau, mais ailleurs dans la mémoire.
Pour une grande majorité d'applications, cette différence n'a pas vraiment d'importance, cependant, dans le code haute performance, cela affectera la localisation des données dans la mémoire et aura un impact important sur les performances du cache CPU. Utiliser des classes quand vous auriez pu / auriez dû utiliser des structures augmentera massivement le nombre de cache manquant sur le CPU.
La chose la plus lente qu'un processeur moderne ne fait pas de croquer les nombres, c'est de récupérer des données de la mémoire, et un hit de cache L1 est beaucoup plus rapide que de lire des données de la RAM.
Voici du code que vous pouvez tester. Sur ma machine, l'itération à travers le tableau de classe prend environ 3 fois plus longtemps que le tableau struct.
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}");
}
Juste pour le compléter, il y a une autre différence lors de l'utilisation du Equals
méthode, qui est héritée par toutes les classes et structures.
Disons que nous avons une classe et une structure:
class A{
public int a, b;
}
struct B{
public int a, b;
}
et dans la méthode Main, nous avons 4 objets.
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;
}
Alors:
s1.Equals(s2) // true
s1.Equals(c1) // false
c1.Equals(c2) // false
c1 == c2 // false
Ainsi , les structures sont adaptées aux objets de type numérique, comme les points (enregistrer les coordonnées x et y). Et les cours conviennent aux autres. Même si 2 personnes ont le même nom, la même taille, le même poids ..., ce sont quand même 2 personnes.
Eh bien, pour commencer, une structure est passée par valeur plutôt que par référence. Les structures sont bonnes pour les structures de données relativement simples, tandis que les classes ont beaucoup plus de flexibilité d'un point de vue architectural via le polymorphisme et l'héritage.
D'autres peuvent probablement vous donner plus de détails que moi, mais j'utilise des structures lorsque la structure que je recherche est simple.
Outre la différence de base du spécificateur d'accès, et quelques-uns mentionnés ci-dessus, je voudrais ajouter quelques-unes des principales différences, y compris quelques-unes des mentionnées ci-dessus avec un exemple de code avec sortie, qui donnera une idée plus claire de la référence et de la valeur
Structures:
Classe:
Exemple de code
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;
}
}
Production
La valeur initiale de Struct Object est: 10
Méthode de structure interne La valeur de méthode interne de l'objet Struct est: 20
Après la valeur d'appel de méthode de l'objet Struct est: 10
La valeur initiale de l'objet de classe est: 10
Méthode de classe interne La valeur de méthode interne de l'objet classe est: 20
Après que la valeur d'appel de méthode de l'objet de classe soit: 20
Ici, vous pouvez clairement voir la différence entre l'appel par valeur et l'appel par référence.
Les événements déclarés dans une classe ont leur accès + = et - = automatiquement verrouillé via un verrou (this) pour les rendre sûrs pour les threads (les événements statiques sont verrouillés sur le type de la classe). Les événements déclarés dans une structure n'ont pas leur accès + = et - = automatiquement verrouillé. Un verrou (ceci) pour une structure ne fonctionnerait pas car vous ne pouvez verrouiller qu'une expression de type référence.
La création d'une instance de structure ne peut pas provoquer un garbage collection (sauf si le constructeur crée directement ou indirectement une instance de type référence) tandis que la création d'une instance de type reference peut provoquer un garbage collection.
Une structure a toujours un constructeur public par défaut intégré.
class DefaultConstructor
{
static void Eg()
{
Direct yes = new Direct(); // Always compiles OK
InDirect maybe = new InDirect(); // Compiles if constructor exists and is accessible
//...
}
}
Cela signifie qu'une structure est toujours instanciable alors qu'une classe peut ne pas l'être car tous ses constructeurs peuvent être privés.
class NonInstantiable
{
private NonInstantiable() // OK
{
}
}
struct Direct
{
private Direct() // Compile-time error
{
}
}
Une structure ne peut pas avoir de destructeur. Un destructeur est juste un remplacement d'objet.Finalize déguisé, et les structures, étant des types de valeur, ne sont pas soumises au garbage collection.
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
Une structure est implicitement scellée, une classe ne l'est pas.
Une structure ne peut pas être abstraite, une classe peut.
Une structure ne peut pas appeler: base () dans son constructeur alors qu'une classe sans classe de base explicite le peut.
Une structure ne peut pas étendre une autre classe, une classe peut.
Une structure ne peut pas déclarer des membres protégés (par exemple, des champs, des types imbriqués) qu'une classe peut.
Une structure ne peut pas déclarer les membres d'une fonction abstraite, une classe abstraite le peut.
Une structure ne peut pas déclarer les membres d'une fonction virtuelle, une classe le peut.
Une structure ne peut pas déclarer des membres de fonction scellés, une classe le peut.
Une structure ne peut pas déclarer les membres d'une fonction de substitution, une classe le peut.
La seule exception à cette règle est qu'une structure peut remplacer les méthodes virtuelles de System.Object, viz, Equals () et GetHashCode () et ToString ().
Object
, qui contiendrait une référence à une copie encadrée de la structure.
Comme mentionné précédemment: les classes sont de type référence tandis que les structures sont des types de valeur avec toutes les conséquences.
Comme un pouce de règle, Framework Design Guidelines recommande d'utiliser Structs au lieu de classes si:
Il y a un cas intéressant de casse-tête "classe vs struct" - situation où vous devez renvoyer plusieurs résultats de la méthode: choisissez celui à utiliser. Si vous connaissez l'histoire ValueTuple - vous savez que ValueTuple (struct) a été ajouté car il devrait être plus efficace que Tuple (classe). Mais qu'est-ce que cela signifie en chiffres? Deux tests: l'un est struct / classe qui a 2 champs, l'autre avec struct / classe qui a 8 champs (avec dimension plus que 4 - la classe devrait devenir plus efficace que struct en termes de tics de processeur, mais bien sûr la charge GC devrait également être considérée ).
PS Une autre référence pour le cas spécifique «sturct ou classe avec collections» est là: 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 |
Test de code:
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;
}
}
}
}
Les structures sont la valeur réelle - elles peuvent être vides mais jamais nulles
Cela est vrai, cependant, notez également que depuis .NET 2, les structures prennent en charge une version Nullable et C # fournit du sucre syntaxique pour le rendre plus facile à utiliser.
int? value = null;
value = 1;
(object)(default(int?)) == null
que vous ne pouvez pas faire avec un autre type de valeur, car il y a plus que du sucre ici. Le seul sucre est int?
pour Nullable<int>
.
Chaque variable ou champ d'un type de valeur primitif ou d'un type de structure contient une instance unique de ce type, y compris tous ses champs (publics et privés). En revanche, des variables ou des champs de types de référence peuvent être nuls ou faire référence à un objet, stocké ailleurs, sur lequel un certain nombre d'autres références peuvent également exister. Les champs d'une structure seront stockés au même endroit que la variable ou le champ de ce type de structure, qui peut être soit sur la pile, soit faire partie d' un autre objet tas.
La création d'une variable ou d'un champ d'un type de valeur primitif le créera avec une valeur par défaut; la création d'une variable ou d'un champ d'un type de structure créera une nouvelle instance, en créant tous les champs de la manière par défaut. La création d'une nouvelle instance d'un type de référence commence par la création par défaut de tous les champs, puis par l'exécution d'un code supplémentaire facultatif en fonction du type.
La copie d'une variable ou d'un champ d'un type primitif dans une autre copiera la valeur. La copie d'une variable ou d'un champ de type de structure vers une autre copiera tous les champs (publics et privés) de l'ancienne instance vers la dernière instance. La copie d'une variable ou d'un champ de type référence dans une autre fera que cette dernière se référera à la même instance que la première (le cas échéant).
Il est important de noter que dans certains langages comme C ++, le comportement sémantique d'un type est indépendant de la façon dont il est stocké, mais ce n'est pas le cas de .NET. Si un type implémente une sémantique de valeur mutable, la copie d'une variable de ce type dans une autre copie les propriétés de la première vers une autre instance, référencée par la seconde, et l'utilisation d'un membre de la seconde pour muter entraînera la modification de cette seconde instance. , mais pas le premier. Si un type implémente une sémantique de référence mutable, la copie d'une variable dans une autre et l'utilisation d'un membre de la seconde pour muter l'objet affecteront l'objet référencé par la première variable; les types avec une sémantique immuable ne permettent pas la mutation, donc peu importe sémantiquement si la copie crée une nouvelle instance ou crée une autre référence à la première.
Dans .NET, il est possible pour les types de valeur d'implémenter l'une des sémantiques ci-dessus, à condition que tous leurs champs puissent faire de même. Un type de référence, cependant, ne peut implémenter que la sémantique de référence mutable ou la sémantique immuable; les types de valeur avec des champs de types de référence mutables sont limités à l'implémentation d'une sémantique de référence mutable ou d'une sémantique hybride étrange.