Quand utiliser struct?

Quand faut-il utiliser struct et non classer en C #? Mon modèle conceptuel est que les structures sont utilisées lorsque l’élément est simplement une collection de types de valeur . Un moyen de les maintenir tous ensemble dans un ensemble cohérent.

Je suis tombé sur ces règles ici :

  • Une structure doit représenter une valeur unique.
  • Une structure doit avoir une empreinte mémoire inférieure à 16 octets.
  • Une structure ne doit pas être modifiée après la création.

Ces règles fonctionnent-elles? Que signifie une structure sémantiquement?

La source référencée par l’OP a une certaine crédibilité … mais qu’en est-il de Microsoft – quelle est la position sur l’utilisation des structures? J’ai cherché un apprentissage supplémentaire auprès de Microsoft , et voici ce que j’ai trouvé:

Envisagez de définir une structure au lieu d’une classe si les instances de ce type sont petites et généralement de courte durée ou sont généralement incorporées dans d’autres objects.

Ne définissez une structure que si le type présente toutes les caractéristiques suivantes:

  1. Il représente logiquement une valeur unique, similaire aux types primitifs (entier, double, etc.).
  2. Il a une taille d’instance inférieure à 16 octets.
  3. C’est immuable.
  4. Il ne sera pas nécessaire de l’encadrer fréquemment.

Microsoft enfreint constamment ces règles

D’accord, # 2 et # 3 de toute façon. Notre dictionnaire bien-aimé a 2 structures internes:

 [StructLayout(LayoutKind.Sequential)] // default for structs private struct Entry // { // View code at *Reference Source } [Serializable, StructLayout(LayoutKind.Sequential)] public struct Enumerator : IEnumerator>, IDisposable, IDictionaryEnumerator, IEnumerator { // View code at *Reference Source } 

* Source de référence

La source ‘JonnyCantCode.com’ a obtenu 3 sur 4 – tout à fait pardonnable puisque le n ° 4 ne serait probablement pas un problème. Si vous vous trouvez en train de boxer une structure, repensez à votre architecture.

Voyons pourquoi Microsoft utiliserait ces structures:

  1. Chaque structure, Entry et Enumerator , représente des valeurs uniques.
  2. La vitesse
  3. Entry n’est jamais transmise en tant que paramètre en dehors de la classe Dictionary. Des recherches plus approfondies montrent que, pour satisfaire l’implémentation de IEnumerable, Dictionary utilise la structure Enumerator qu’il copie chaque fois qu’un énumérateur est demandé … a du sens.
  4. Interne à la classe Dictionary. Enumerator est public car Dictionary est énumérable et doit avoir une accessibilité égale à l’implémentation de l’interface IEnumerator – par exemple IEnumerator getter.

Mise à jour – En outre, sachez que lorsqu’une structure implémente une interface – comme Enumerator le fait – et qu’elle est convertie dans ce type implémenté, la structure devient un type de référence et est déplacée vers le tas. En interne à la classe Dictionary, Enumerator est toujours un type de valeur. Toutefois, dès qu’une méthode appelle GetEnumerator() , un IEnumerator type référence est renvoyé.

Ce que nous ne voyons pas ici, c’est une tentative ou une preuve de la nécessité de garder les structures immuables ou de conserver une taille d’instance de seulement 16 octets ou moins:

  1. Rien dans les structures ci-dessus n’est déclaré en readonlypas immuable
  2. La taille de ces structures peut dépasser 16 octets
  3. Entry a une durée de vie indéterminée (de Add() , à Remove() , Clear() , ou garbage collection);

Et … 4. Les deux structures stockent TKey et TValue, que nous soaps tous très bien être des types de référence (informations supplémentaires sur les bonus)

Malgré les clés hachées, les dictionnaires sont rapides en partie car l’instanciation d’une structure est plus rapide qu’un type de référence. Ici, j’ai un Dictionary qui stocke 300 000 entiers aléatoires avec des clés séquentiellement incrémentées.

Capacité: 312874
MemSize: 2660827 octets
Terminé Redimensionner: 5ms
Temps total pour remplir: 889ms

Capacity : nombre d’éléments disponibles avant le redimensionnement du tableau interne.

MemSize : déterminé en sérialisant le dictionnaire dans un MemoryStream et en obtenant une longueur en octets (suffisamment précise pour nos besoins).

Terminé Redimensionner : temps nécessaire pour redimensionner le tableau interne de 150862 éléments à 312874 éléments. Lorsque vous pensez que chaque élément est copié séquentiellement via Array.CopyTo() , ce n’est pas trop grave.

Temps total de remplissage : certes biaisé en raison de la journalisation et d’un événement OnResize j’ai ajouté à la source; Cependant, toujours impressionnant pour remplir 300k entiers tout en redimensionnant 15 fois pendant l’opération. Juste par curiosité, quel serait le temps total à remplir si je connaissais déjà la capacité? 13ms

Alors, maintenant, si l’ Entry était une classe? Ces temps ou mésortingques diffèrent-ils vraiment autant?

Capacité: 312874
MemSize: 2660827 octets
Terminé Redimensionner: 26ms
Temps total pour remplir: 964ms

De toute évidence, la grande différence réside dans le redimensionnement. Toute différence si Dictionary est initialisé avec la capacité? Pas assez pour se soucier de … 12ms .

Qu’est-ce qui se passe est que, comme Entry est une structure, il ne nécessite pas d’initialisation comme un type de référence. C’est à la fois la beauté et le fléau du type valeur. Pour pouvoir utiliser Entry comme type de référence, j’ai dû insérer le code suivant:

 /* * 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( ); } /* *********************************************** */ 

La raison pour laquelle je devais initialiser chaque élément de tableau de Entry tant que type de référence peut être trouvé sur MSDN: Structure Design . En bref:

Ne fournissez pas de constructeur par défaut pour une structure.

Si une structure définit un constructeur par défaut, lorsque des tableaux de la structure sont créés, le Common Language Runtime exécute automatiquement le constructeur par défaut sur chaque élément du tableau.

Certains compilateurs, tels que le compilateur C #, ne permettent pas aux structures d'avoir des constructeurs par défaut.

C'est en fait assez simple et nous emprunterons les Trois lois de la robotique d'Asimov :

  1. La structure doit être sûre à utiliser
  2. La structure doit remplir sa fonction efficacement, à moins que cela ne viole la règle n ° 1
  3. La structure doit restr intacte pendant son utilisation, à moins que sa destruction ne soit requirejse pour satisfaire à la règle n ° 1

... qu'est-ce que nous en retirons : bref, être responsable de l'utilisation des types de valeur. Ils sont rapides et efficaces, mais ils peuvent provoquer de nombreux comportements inattendus s'ils ne sont pas correctement entretenus (copies non intentionnelles).

Lorsque vous n’avez pas besoin de polymorphism, recherchez la sémantique des valeurs et évitez l’allocation de segment de mémoire et la surcharge de récupération de place associée. La mise en garde, cependant, est que les structures (arbitrairement grandes) sont plus coûteuses à transmettre que les références de classe (généralement un mot machine), de sorte que les classes pourraient finir par être plus rapides dans la pratique.

Je ne suis pas d’accord avec les règles données dans le message original. Voici mes règles:

1) Vous utilisez des structures pour les performances lorsqu’elles sont stockées dans des tableaux. (voir aussi Quand les structs sont-ils la réponse? )

2) Vous en avez besoin dans le code en passant des données structurées à / de C / C ++

3) N’utilisez pas de structs sauf si vous en avez besoin:

  • Ils se comportent différemment des “objects normaux” ( types de référence ) en cours d’affectation et lors du passage en argument, ce qui peut entraîner un comportement inattendu. Cela est particulièrement dangereux si la personne qui regarde le code ne sait pas qu’elle a affaire à une structure.
  • Ils ne peuvent pas être hérités.
  • Passer des structures en arguments est plus coûteux que les classes.

Utilisez une structure lorsque vous souhaitez utiliser la sémantique de valeur par opposition à la sémantique de référence.

modifier

Je ne suis pas sûr de savoir pourquoi les gens ont opté pour cette méthode, mais il s’agit là d’un point valable, avant que l’opération ne clarifie sa question, et c’est la raison fondamentale de la structure.

Si vous avez besoin d’une sémantique de référence, vous avez besoin d’une classe et non d’une structure.

En plus de la réponse “c’est une valeur”, un scénario spécifique pour l’utilisation de structures est lorsque vous savez que vous avez un dataset à l’origine de problèmes de récupération de place et que vous avez beaucoup d’objects. Par exemple, une grande liste / un tableau d’instances de personne. La métaphore naturelle ici est une classe, mais si vous avez un grand nombre d’instances de personnes à vie longue, elles peuvent finir par obstruer GEN-2 et causer des blocages de GC. Si le scénario le justifie, une approche potentielle consiste à utiliser un tableau (et non une liste) de structures Person, à savoir Person[] . Maintenant, au lieu d’avoir des millions d’objects dans GEN-2, vous avez un seul morceau sur le LOH (je suppose qu’il n’y a pas de chaînes, ici, c’est-à-dire une valeur pure sans aucune référence). Cela a très peu d’impact sur le GC.

Travailler avec ces données est gênant, car les données sont probablement surdimensionnées pour une structure, et vous ne voulez pas copier des valeurs volumineuses en permanence. Cependant, y accéder directement dans un tableau ne copie pas la structure – elle est en place (contrairement à un indexeur de liste, qui ne copie pas). Cela signifie beaucoup de travail avec les index:

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

Notez que garder les valeurs elles-mêmes immuables aidera ici. Pour une logique plus complexe, utilisez une méthode avec un paramètre by-ref:

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

Encore une fois, c’est en place – nous n’avons pas copié la valeur.

Dans des scénarios très spécifiques, cette tactique peut être très efficace. Cependant, c’est un scénario assez avancé qui ne devrait être tenté que si vous savez ce que vous faites et pourquoi. La valeur par défaut serait une classe.

De la spécification du langage C # :

1.7 Structures

Comme les classes, les structures sont des structures de données pouvant contenir des membres de données et des membres de fonction, mais contrairement aux classes, les structures sont des types de valeur et ne nécessitent pas d’allocation de segment. Une variable de type struct stocke directement les données de la structure, tandis qu’une variable de type classe stocke une référence à un object alloué dynamicment. Les types de structure ne prennent pas en charge l’inheritance spécifié par l’utilisateur et tous les types de structure héritent implicitement de type object.

Les structures sont particulièrement utiles pour les petites structures de données qui ont une sémantique de valeur. Les nombres complexes, les points dans un système de coordonnées ou les paires clé-valeur dans un dictionnaire sont tous de bons exemples de structures. L’utilisation de structures plutôt que de classes pour de petites structures de données peut faire une grande différence dans le nombre d’allocations de mémoire effectuées par une application. Par exemple, le programme suivant crée et initialise un tableau de 100 points. Avec Point implémenté en tant que classe, 101 objects distincts sont instanciés – un pour le tableau et un pour chacun des 100 éléments.

 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); } } 

Une alternative consiste à faire de Point une structure.

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

Maintenant, un seul object est instancié - celui du tableau - et les instances Point sont stockées en ligne dans le tableau.

Les constructeurs Struct sont appelés avec le nouvel opérateur, mais cela ne signifie pas que la mémoire est allouée. Au lieu d'allouer dynamicment un object et de lui renvoyer une référence, un constructeur struct renvoie simplement la valeur de la structure elle-même (généralement dans un emplacement temporaire de la stack), puis cette valeur est copiée si nécessaire.

Avec les classes, il est possible que deux variables référencent le même object et donc que des opérations sur une variable affectent l'object référencé par l'autre variable. Avec les structs, les variables ont chacune leur propre copie des données, et il est impossible que les opérations sur l'une affectent l'autre. Par exemple, la sortie produite par le fragment de code suivant dépend de si Point est une classe ou une structure.

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

Si Point est une classe, la sortie est 20 car a et b font référence au même object. Si Point est une structure, la sortie est 10 car l'affectation de a à b crée une copie de la valeur et cette copie n'est pas affectée par l'affectation suivante à ax

L'exemple précédent met en évidence deux des limitations des structures. Tout d'abord, la copie d'une structure entière est généralement moins efficace que la copie d'une référence d'object, de sorte que le passage des parameters d'affectation et de valeur peut être plus coûteux avec les structures qu'avec les types de référence. Deuxièmement, à l'exception des parameters ref et out, il n'est pas possible de créer des références aux structures, ce qui exclut leur utilisation dans un certain nombre de situations.

Les structures sont bonnes pour la représentation atomique des données, où les données peuvent être copiées plusieurs fois par le code. Le clonage d’un object est généralement plus coûteux que la copie d’une structure, car il implique l’allocation de la mémoire, l’exécution du constructeur et la désallocation / garbage collection une fois terminé.

Voici une règle de base.

  • Si tous les champs membres sont des types valeur, créez une structure .

  • Si un champ membre est un type de référence, créez une classe . C’est parce que le champ de type de référence aura besoin de l’allocation de tas de toute façon.

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 ssortingng Name; // Reference Type } 

Tout d’abord: scénarios d’interopérabilité ou lorsque vous devez spécifier la disposition de la mémoire

Deuxièmement: lorsque les données ont presque la même taille qu’un pointeur de référence.

Vous devez utiliser une “structure” dans les situations où vous souhaitez spécifier explicitement la disposition de la mémoire à l’aide de StructLayoutAtsortingbute , généralement pour PInvoke.

Edit: Le commentaire indique que vous pouvez utiliser class ou struct avec StructLayoutAtsortingbute et c’est certainement vrai. En pratique, vous utiliserez généralement une structure – elle est allouée à la stack par rapport au segment de mémoire, ce qui est logique si vous transmettez un argument à un appel de méthode non géré.

J’utilise des structures pour emballer ou déballer tout type de format de communication binary. Cela inclut la lecture ou l’écriture sur disque, les listes de sumts DirectX, les protocoles réseau ou le traitement de données chiffrées / compressées.

Les trois lignes direcsortingces que vous avez énumérées ne m’ont pas été utiles dans ce contexte. Quand j’ai besoin d’écrire quatre cents octets de choses dans un ordre particulier, je vais définir une structure de quatre cents octets, et je vais la remplir avec toutes les valeurs sans rapport qu’elle est censée avoir, et je vais pour le mettre en place de la manière la plus logique à l’époque. (D’accord, quatre cents octets seraient assez étranges, mais à l’époque où j’écrivais des fichiers Excel pour gagner ma vie, je traitais des structures jusqu’à une quarantaine d’octets, car c’est la taille de certains enregistrements BIFF.)

À l’exception des types de valeurs utilisés directement par le moteur d’exécution et de divers autres pour les besoins de PInvoke, vous ne devez utiliser que des types de valeurs dans deux scénarios.

  1. Lorsque vous avez besoin de sémantique de copie.
  2. Lorsque vous avez besoin d’une initialisation automatique, normalement dans les tableaux de ces types.

.NET prend en charge les value types et reference types (en Java, vous ne pouvez définir que des types de référence). Les instances de reference types de reference types sont allouées dans le segment de mémoire géré et sont collectées lorsque aucune référence ne leur est atsortingbuée. Les instances de value types de value types , par contre, sont allouées dans la stack et, par conséquent, la mémoire allouée est récupérée dès que leur scope se termine. Et bien sûr, les value types sont passés par valeur et reference types de référence par référence. Tous les types de données primitifs C #, à l’exception de System.Ssortingng, sont des types de valeur.

Quand utiliser struct over class,

En C #, les structs sont des value types , les classes sont reference types . Vous pouvez créer des types de valeur, en C #, à l’aide du mot-clé enum et du mot struct clé struct . L’utilisation d’un value type au lieu d’un reference type entraînera une réduction du nombre d’objects sur le segment de mémoire géré, ce qui se traduira par une charge moindre sur le récupérateur de mémoire, des cycles de GC moins fréquents et, par conséquent, de meilleures performances. Cependant, les value types ont aussi leurs inconvénients. Passer une grosse struct est certainement plus coûteux que de passer une référence, c’est un problème évident. L’autre problème est la surcharge associée à la boxing/unboxing . Dans le cas où vous vous demandez ce que signifie boxing/unboxing , suivez ces liens pour une bonne explication sur la boxing et le unboxing . En dehors des performances, il y a des moments où vous avez simplement besoin de types ayant une sémantique de valeur, ce qui serait très difficile (ou moche) à implémenter si vous ne disposez que de reference types . Vous ne devez utiliser value types , lorsque vous avez besoin d’une sémantique de copie ou que vous avez besoin d’une initialisation automatique, normalement dans les arrays de ces types.

Structure types in C# or other .net languages are generally used to hold things that should behave like fixed-sized groups of values. A useful aspect of structure types is that the fields of a structure-type instance can be modified by modifying the storage location in which it is held, and in no other way. It’s possible to code a structure in such a way that the only way to mutate any field is to construct a whole new instance and then use a struct assignment to mutate all the fields of the target by overwriting them with values from the new instance, but unless a struct provides no means of creating an instance where its fields have non-default values, all of its fields will be mutable if and if the struct itself is stored in a mutable location.

Note that it’s possible to design a structure type so that it will essentially behave like a class type, if the structure contains a private class-type field, and redirects its own members to that of the wrapped class object. For example, a PersonCollection might offer properties SortedByName and SortedById , both of which hold an “immutable” reference to a PersonCollection (set in their constructor) and implement GetEnumerator by calling either creator.GetNameSortedEnumerator or creator.GetIdSortedEnumerator . Such structs would behave much like a reference to a PersonCollection , except that their GetEnumerator methods would be bound to different methods in the PersonCollection . One could also have a structure wrap a portion of an array (eg one could define an ArrayRange structure which would hold a T[] called Arr , an int Offset , and an int Length , with an indexed property which, for an index idx in the range 0 to Length-1 , would access Arr[idx+Offset] ). Unfortunately, if foo is a read-only instance of such a structure, current comstackr versions won’t allow operations like foo[3]+=4; because they have no way to determine whether such operations would attempt to write to fields of foo .

It’s also possible to design a structure to behave a like a value type which holds a variable-sized collection (which will appear to be copied whenever the struct is) but the only way to make that work is to ensure that no object to which the struct holds a reference will ever be exposed to anything which might mutate it. For example, one could have an array-like struct which holds a private array, and whose indexed “put” method creates a new array whose content is like that of the original except for one changed element. Unfortunately, it can be somewhat difficult to make such structs perform efficiently. While there are times that struct semantics can be convenient (eg being able to pass an array-like collection to a routine, with the caller and callee both knowing that outside code won’t modify the collection, may be better than requiring both caller and callee to defensively copy any data they’re given), the requirement that class references point to objects that will never be mutated is often a pretty severe constraint.

Nah – I don’t entirely agree with the rules. They are good guidelines to consider with performance and standardization, but not in light of the possibilities.

As you can see in the responses, there are a log of creative ways to use them. So, these guidelines need to just be that, always for the sake of performance and efficiency.

In this case, I use classes to represent real world objects in their larger form, I use structs to represent smaller objects that have more exact uses. The way you said it, “a more cohesive whole.” The keyword being cohesive. The classes will be more object oriented elements, while structs can have some of those characteristics, their on a smaller scale. IMO.

I use them a lot putting in Treeview and Listview tags where common static atsortingbutes can be accessed very quickly. I would struggle to get this info another way. For example, in my database applications, I use a Treeview where I have Tables, SPs, Functions, or any other objects. I create and populate my struct, put it in the tag, pull it out, get the data of the selection and so forth. I wouldn’t do this with a class!

I do try and keep them small, use them in single instance situations, and keep them from changing. It’s prudent to be aware of memory, allocation, and performance. And testing is so necessary.

A struct is a value type. If you assign a struct to a new variable, the new variable will contain a copy of the original.

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

Excecution of the following results in 5 instances of the struct stored in memory:

 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. 

A class is a reference type. When you assign a class to a new variable, the variable contains a reference to the original class object.

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

Excecution of the following results in only one instance of the class object in memory.

 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 may increase the likelihood of a code mistake. If a value object is treated like a mutable reference object, a developer may be surprised when changes made are unexpectedly lost.

 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 

I made a small benchmark with BenchmarkDotNet to get a better understanding of “struct” benefit in numbers. I’m testing looping through array (or list) of structs (or classes). Creating those arrays or lists is out of the benchmark’s scope – it is clear that “class” is more heavy will utilize more memory, and will involve GC.

So the conclusion is: be careful with LINQ and hidden structs boxing/unboxing and using structs for microoptimizations ssortingctly stay with arrays.

PS Another benchmark about passing struct/class through call stack is there 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 | 

Code:

 [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn] [ClrJob, CoreJob] [HtmlExporter, MarkdownExporter] [MemoryDiagnoser] public class BenchmarkRef { public class C1 { public ssortingng Text1; public ssortingng Text2; public ssortingng Text3; } public struct S1 { public ssortingng Text1; public ssortingng Text2; public ssortingng Text3; } List testListClass = new List(); List testListStruct = new List(); 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; } } 

My rule is

1, Always use class;

2, If there is any performance issue, I try to change some class to struct depending on the rules which @IAbstract mentioned, and then do a test to see if these changes can improve performance.

A class is a reference type. When an object of the class is created, the variable to which the object is assigned holds only a reference to that memory. When the object reference is assigned to a new variable, the new variable refers to the original object. Changes made through one variable are reflected in the other variable because they both refer to the same data. A struct is a value type. When a struct is created, the variable to which the struct is assigned holds the struct’s actual data. When the struct is assigned to a new variable, it is copied. The new variable and the original variable therefore contain two separate copies of the same data. Changes made to one copy do not affect the other copy. In general, classes are used to model more complex behavior, or data that is intended to be modified after a class object is created. Structs are best suited for small data structures that contain primarily data that is not intended to be modified after the struct is created.

Classes and Structs (C# Programming Guide)

I think a good first approximation is “never”.

I think a good second approximation is “never”.

If you are desperate for perf, consider them, but then always measure.

I was just dealing with Windows Communication Foundation [WCF] Named Pipe and I did notice that it does make sense to use Structs in order to ensure that exchange of data is of value type instead of reference type .

Struct can be used to improve garbage collection performance. While you usually don’t have to worry about GC performance, there are scenarios where it can be a killer. Like large caches in low latency applications. See this post for an example:

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

Structure or value types can be used in following scenarios –

  1. If you want to prevent the object to be collected by garbage collection.
  2. If it is a simple type and no member function modifies its instance fields
  3. If there is no need to derive from other types or being derived to other types.

You can know more about the value types and values types here on this link

Briefly, use struct if :

1- your object properties/fields do not need to be changed. I mean you just want to give them an initial value and then read them.

2- properties and fields in your object are value type and they are not so large.

If that’s the case you can take advantage of structs for a better performance and optimized memory allocation as they use only stacks rather than both stacks and heaps (in classes)

The C# struct is a lightweight alternative to a class. It can do almost the same as a class, but it’s less “expensive” to use a struct rather than a class. The reason for this is a bit technical, but to sum up, new instances of a class is placed on the heap, where newly instantiated structs are placed on the stack. Furthermore, you are not dealing with references to structs, like with classes, but instead you are working directly with the struct instance. This also means that when you pass a struct to a function, it is by value, instead of as a reference. There is more about this in the chapter about function parameters.

So, you should use structs when you wish to represent more simple data structures, and especially if you know that you will be instantiating lots of them. There are lots of examples in the .NET framework, where Microsoft has used structs instead of classes, for instance the Point, Rectangle and Color struct.

I rarely use a struct for things. But that’s just me. It depends whether I need the object to be nullable or not.

As stated in other answers, I use classes for real-world objects. I also have the mindset of structs are used for storing small amounts of data.

It seems to me that struct have no strong semantic that give the user a strong idea of when to use it.

It resemble as a class but lake most of its feature. It is a kind of degraded version of a class. There is a lot of says about when not use it but very few on when to use it.

IMO, there is no reason why struct should be implemented in a OO language at the first place. Actually primitive type should not exist in a pure OO language but I digress.

It might be a way to optimize stuff. A kind of boxing free things that can be optimize on some call site.

My 2 cent, I would say that it violated the KISS of the language principle and avoid it as much has I can.

Structures are in most ways like classes/objects. Structure can contain functions, members and can be inherited. But structures are in C# used just for data holding . Structures does take less RAM than classes and are easier for garbage collector to collect . But when you use functions in your structure, then comstackr actually takes that structure very similarly as class/object, so if you want something with functions, then use class/object .