Quand utiliser struct?

Quand devez-vous utiliser struct et non class in C#? Mon modèle conceptuel est que les structures sont utilisées en temps lorsque l'élément est simplement une collection de types de valeur . Une façon logique de les réunir en un tout cohérent.

je suis tombé sur ces règles ici :

  • une structure doit représenter un valeur.
  • Une structure doit avoir une mémoire empreinte moins de 16 octets.
  • une structure ne doit pas être changée après création.

ces règles fonctionnent-elles? Qu'est-ce qu'une structure signifie sémantiquement?

1216
demandé sur Mark Hurd 2009-02-06 20:37:55

28 réponses

la source mentionnée par L'OP a une certaine crédibilité ...mais qu'en est Microsoft - quelle est la position sur struct utilisation? J'ai cherché quelques extra apprendre de Microsoft , et voici ce que j'ai trouvé:

envisager de définir une structure au lieu d'une classe si les instances de la type sont petites et généralement de courte durée ou sont généralement d'autres objets.

ne définit pas une structure à moins que le type ne possède toutes les caractéristiques suivantes:

  1. il représente logiquement une valeur unique, similaire aux types primitifs (entier, double, et ainsi de suite).
  2. il a une taille d'instance inférieure à 16 octets.
  3. c'est immuable.
  4. il ne devra pas être boxé fréquemment.

Microsoft viole systématiquement ces règles

OK, #2 et # 3 en tout cas. Notre dictionnaire bien-aimé a 2 structures internes:

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

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

* Source De Référence

The "JonnyCantCode.com' source a obtenu 3 sur 4 - Assez pardonnable car #4 ne serait probablement pas un problème. Si vous vous retrouvez à boxer une structure, repensez votre architecture.

regardons Pourquoi Microsoft utiliserait ces structures:

  1. chaque structure, Entry et Enumerator , représentent des valeurs uniques.
  2. vitesse
  3. Entry n'est jamais passé comme paramètre en dehors de la classe du dictionnaire. Une étude plus approfondie montre que pour satisfaire à la mise en oeuvre de IEnumerable, le dictionnaire utilise la structure Enumerator qu'il copie chaque fois qu'un recenseur est demandé ...du sens.
  4. interne à la classe du dictionnaire. Enumerator est public parce que le dictionnaire est énumérable et doit avoir une accessibilité égale à l'IEnumerator interface implementation - e.g. IEnumerator getter.

Update - en outre, réalisez que lorsqu'une structure implémente une interface - comme le fait un recenseur - et est moulée à ce type implémenté, la structure devient un type de référence et est déplacée vers le tas. Interne à la classe de dictionnaire, énumérateur est encore un type de valeur. Toutefois, dès qu'une méthode appelle GetEnumerator() , un type de référence IEnumerator est retourné.

ce que nous ne voyons pas ici est une tentative ou une preuve d'exigence de garder des structures immuables ou de maintenir une taille d'instance de seulement 16 octets ou moins:

  1. rien dans les structures ci-dessus n'est déclaré readonly - pas immuable
  2. Taille de ces struct pourrait bien être plus de 16 octets
  3. Entry a une durée de vie indéterminée (de Add() à Remove() , Clear() , ou collecte des ordures);

et ... 4. Les deux structures stockent TKey et TValue, qui, nous le savons tous, sont tout à fait capables d'être des types de référence (bonus info)

malgré les clés hachées, les dictionnaires sont rapides en partie parce que instancier une structure est plus rapide qu'un type de référence. Ici, j'ai un Dictionary<int, int> qui stocke 300 000 entiers aléatoires avec des clés incrémentées séquentiellement.

capacité: 312874

MemSize: 2660827 octets

Complété redimensionner: 5ms

Temps Total de remplissage: 889ms

capacité : nombre d'éléments disponible avant le tableau interne doit être redimensionnée.

MemSize : déterminé en sérialisant le dictionnaire dans un flux de mémoire et en obtenant une longueur d'octet (assez précise pour nos buts).

Terminé Redimensionner : le temps qu'il faut pour redimensionner le tableau interne de 150862 éléments de 312874 éléments. Quand on pense que chaque élément est copié de façon séquentielle via Array.CopyTo() , ce n'est pas trop shabby.

Total time to fill : certes biaisé en raison de la journalisation et un OnResize événement que j'ai ajouté à la source; cependant, toujours impressionnant à remplir 300K entiers tout en redimensionnant 15 fois au cours de l'opération. Juste par curiosité, quel serait le temps total à remplir si je connaissais déjà la capacité? 13ms

et si Entry était une classe? Ces moments ou métriques vraiment diffèrent à ce point?

capacité: 312874

MemSize: 2660827 octets

Redimensionnement achevé: 26ms

Temps Total de remplissage: 964ms

évidemment, la grande différence est dans le redimensionnement. Aucune différence si le Dictionnaire est initialisé avec la Capacité? Pas assez d'inquiétude ... 12ms .

ce qui se passe, c'est que Entry est une structure, elle 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 de valeur. Pour 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 j'ai dû initialiser chaque élément de tableau de Entry comme un type de référence peut être trouvé à MSDN: Structure Design . En bref:

ne fournissent pas de constructeur par défaut pour une structure.

Si une structure définit un constructeur par défaut, lorsque les tableaux de la la structure est créée, le langage courant exécute automatiquement exécute le constructeur par défaut sur chaque élément du tableau.

certains compilateurs, comme le compilateur C#, ne permettent pas aux structures de avez constructeurs par défaut.

il est en fait très simple et nous emprunterons de Asimov's trois lois de la robotique :

  1. La structure doit être sûr d'utiliser
  2. La structure doit exercer sa fonction de manière efficace, à moins que cela violerait la règle n ° 1
  3. La structure doit rester intacte pendant son utilisation, à moins que sa destruction est nécessaire pour satisfaire à la règle n ° 1

... qu'est-ce que nous retirons de ce : en bref, être responsable avec l'utilisation de types de valeur. Ils sont rapides et efficaces, mais ont la capacité de causer de nombreux comportements inattendus si elles ne sont pas correctement maintenues (c.-à-d. des copies involontaires).

548
répondu IAbstract 2015-06-11 13:53:06

chaque fois que vous n'avez pas besoin de polymorphisme, vous voulez une sémantique de valeur, et vous voulez éviter l'allocation de tas et la collecte des ordures associée à frais généraux. 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.

145
répondu dsimcha 2009-02-06 17:50:16

je ne suis pas d'accord avec les règles données dans le post original. Voici mes règles:

1) vous utilisez des structures pour la performance lorsqu'elles sont stockées dans des tableaux. (voir aussi quand la réponse est-elle structurée? )

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

3) N'utilisez pas de structures sauf si vous en avez besoin:

  • ils se comportent différemment des "objets normaux"" ( types de référence ) en cours de cession et en passant comme argument, ce qui peut conduire à un comportement inattendu; ceci est particulièrement dangereux si la personne qui regarde le code ne savent pas qu'ils ont affaire à une struct.
  • ils ne peuvent pas être hérités.
  • passer des structures comme arguments est plus cher que des classes.
135
répondu ILoveFortran 2017-05-23 12:18:26

utilisez une structure lorsque vous voulez une sémantique de valeur par opposition à une sémantique de référence.

Modifier

Je ne sais pas pourquoi les gens sont en baisse cela, mais c'est un point valable, et a été fait avant que l'op a clarifié sa question, et il est la raison la plus fondamentale de base pour une structure.

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

82
répondu JoshBerke 2013-08-28 10:10:45

en plus de la réponse" c'est une valeur", un scénario spécifique pour l'utilisation de structures est quand vous savoir que vous avez un ensemble de données qui provoque des problèmes de collecte des ordures, et vous avez beaucoup d'objets. Par exemple, une grande liste/tableau d'instances de personnes. La métaphore naturelle ici est une classe, mais si vous avez un grand nombre d'instance de personne de longue durée, ils peuvent finir par obstruer GEN-2 et causer des décrochages GC. Si le scénario le justifie il, une approche potentielle ici est d'utiliser un tableau (pas de liste) de la personne structures , i.e. Person[] . Maintenant, au lieu d'avoir des millions d'objets en GEN-2, vous avez un seul morceau sur le LOH (je suppose qu'il n'y a pas de chaîne ici - c.-à-d. une valeur pure sans aucune référence). Cela a très peu d'impact sur le GC.

travailler avec ces données est embarrassant, car les données sont probablement surdimensionnées pour une structure, et vous ne voulez pas copier les valeurs de fat tout le temps. Cependant, le fait d'y accéder directement dans un tableau ne copie pas la structure - il est en place (contrairement à un indexeur de liste, qui copie). Cela signifie beaucoup de travail avec des 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 réussi; cependant, c'est un scernario assez avancé qui devrait être tenté que si vous savez ce que vous faites et pourquoi. La valeur par défaut serait ici une classe.

54
répondu Marc Gravell 2011-10-22 12:14:22

De la spécification du Langage C# :

1.7 structures

comme les classes, les structures sont des structures de données qui peuvent contenir des membres de données et des membres de fonctions, mais contrairement aux classes, les structures sont les types valeur et ne nécessitent pas d'allocation de tas. Une variable d'une structure tapez directement stocke les données de la structure, alors qu'une variable d'un type de classe stocke une référence à un alloués dynamiquement objet. Les types de Struct ne prennent pas en charge l'héritage spécifié par l'utilisateur, et tous les types de struct les types héritent implicitement de l'objet type.

Les structures

sont particulièrement utiles pour les petites structures de données qui ont valeur sémantique. Des nombres complexes, des points dans un système de coordonnées, ou paires clé-valeur dans un dictionnaire sont tous de bons exemples de structures. Le l'utilisation de structures plutôt que de classes pour les petites structures de données peuvent faire une grande différence dans le nombre de allocations de mémoire une application effectuer. Par exemple, le programme suivant crée et initialise un tableau de 100 points. Avec Point implémenté comme une classe, 101 les objets séparés sont instanciés-un pour le tableau et un chacun pour les 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 est de faire un point un struct.

struct Point
{
   public int x, y;

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

maintenant, un seul objet est instancié-celui pour le tableau-et les instances de Point sont stockées en ligne dans le tableau.

Les constructeurs de structures

sont invoqués avec le nouvel opérateur, mais cela ne signifie pas que la mémoire est affectée. Au lieu d'allouer dynamiquement un objet et de lui renvoyer une référence, un constructeur de struct renvoie simplement la valeur de struct elle-même (généralement dans un emplacement temporaire sur la pile), et cette valeur est ensuite copiée si nécessaire.

avec classes, il est possible pour deux variables de faire référence au même objet et ainsi possible pour des opérations sur une variable d'affecter l'objet référencé par l'autre variable. Avec les structures, les variables ont chacun leur propre copie des données, et il n'est pas possible pour les opérations sur l'un affecte 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;
a.x = 20;
Console.WriteLine(b.x);

si le Point est une classe, la sortie est 20 parce que a et b font référence au même objet. Si Point est une structure, le la sortie est 10 parce que la cession de a à b crée une copie de la valeur, et cette copie n'est pas affectée par la cession subséquente à A. x.

L'exemple précédent met en évidence deux limites des structures. Premièrement, copier une structure entière est généralement moins efficace que Copier une référence d'objet, de sorte que la transmission des paramètres d'affectation et de valeur peut être plus coûteuse avec les structures qu'avec les types de référence. Deuxièmement, à l'exception des paramètres ref et out, il n'est pas possible de créer des références à des structures, ce qui exclut leur utilisation dans un certain nombre de situations.

36
répondu bUKaneer 2012-09-17 15:42:08

structures sont bonnes pour la représentation atomique des données, où les dites données peuvent être copiées plusieurs fois par le code. Le clonage d'un objet est en général plus coûteux que la copie d'une structure, car il implique l'attribution de la mémoire, l'exécution du constructeur et le désallocation/ramassage des déchets lorsqu'il est fait avec.

31
répondu Franci Penov 2009-02-06 17:58:07

Voici une règle de base.

  • si tous les champs membres sont des types de valeur, créer une struct .

  • si l'un des champs d'un membre est un type de référence, créer 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 string Name; // Reference Type
}
24
répondu Usman Zafar 2014-01-22 10:17:43

D'abord: scénarios Interop ou lorsque vous devez spécifier la disposition de la mémoire

Deuxième: Lorsque les données sont presque de la même taille qu'un pointeur de référence, de toute façon.

17
répondu BC. 2009-02-06 18:12:58

vous devez utiliser une" struct "dans les situations où vous voulez spécifier explicitement la disposition de la mémoire en utilisant la StructLayoutAttribute - typiquement pour PInvoke.

Edit: Comment souligne que vous pouvez utiliser class ou struct avec StructLayoutAttribute et c'est certainement vrai. En pratique, vous utiliserez typiquement une structure-elle est attribuée sur la pile vs le tas ce qui a du sens si vous passez simplement un argument à une méthode non gérée appeler.

16
répondu Maurice Flanagan 2009-02-06 18:41:35

j'utilise des structures pour emballer ou déballer toute sorte de format de communication binaire. Cela inclut la lecture ou l'écriture sur le disque, les listes de vertex DirectX, les protocoles réseau, ou le traitement de données chiffrées/compressées.

les trois lignes directrices que vous énumérez ne m'ont pas été utiles dans ce contexte. Quand j'ai besoin d'écrire 400 octets de trucs dans un ordre particulier, je vais définir une structure de 400 octets, et je vais la remplir avec n'importe quelles valeurs sans rapport. c'est supposé l'être, et je vais l'organiser de la façon la plus logique à l'époque. (OK, 400 octets serait assez étrange-- mais quand j'écrivais des fichiers Excel pour vivre, j'avais affaire à des structures allant jusqu'à une quarantaine d'octets partout, parce que c'est la taille de certains des enregistrements BIFF.)

15
répondu mjfgates 2009-02-06 18:25:49

à l'exception des types de valeur qui sont utilisés directement par l'exécution et divers autres pour les besoins de PInvoke, vous ne devez utiliser les types de valeur que dans 2 scénarios.

  1. quand vous avez besoin de sémantique de copie.
  2. lorsque vous avez besoin d'une initialisation automatique, normalement dans les tableaux de ce type.
14
répondu leppie 2009-02-06 18:15:06

.net supporte value types et reference types (en Java, vous pouvez définir seulement les types de référence). Les cas de reference types sont attribués dans le tas géré et sont des ordures collectées lorsqu'il n'y a pas de références en suspens. Les Instances de value types , d'autre part , sont attribuées dans le stack , et donc la mémoire attribuée est récupérée dès que leur portée se termine. Et bien sûr, value types passe par la valeur, et reference types par référence. Tous les C# primitive types de données, à l'exception du système.Chaîne, sont des types de valeur.

quand utiliser struct sur la classe,

En C#, structs sont value types , les classes sont reference types . Vous pouvez créer des types de valeur, en C#, en utilisant le mot-clé enum et le mot-clé struct . L'utilisation d'un value type au lieu d'un reference type permettra de réduire le nombre d'objets sur le tas géré, ce qui se traduit par une charge moindre sur le collecteur d'ordures (GC), des cycles de GC moins fréquents, et par conséquent une meilleure performance. Cependant, value types a aussi ses inconvénients. Passer un grand struct est certainement plus coûteux que passer une référence, c'est un problème évident. L'autre problème est celui des frais généraux associés à boxing/unboxing . Si vous vous demandez ce que boxing/unboxing signifie, suivez ces liens pour une bonne explication sur boxing et unboxing . En dehors de la performance, Il ya des moments où vous avez simplement besoin de types avoir la sémantique de valeur, qui serait très difficile (ou laid) à mettre en œuvre si reference types sont tout ce que vous avez. Vous devez utiliser value types seulement, lorsque vous avez besoin de la sémantique de copie ou besoin de l'initialisation automatique, normalement dans arrays de ces types.

13
répondu Sujit 2012-09-17 11:21:43
Les types de Structure

dans C# ou d'autres langages.Net sont généralement utilisés pour contenir des choses qui devraient se comporter comme des groupes de valeurs de taille fixe. Un aspect utile des types de structure est que les champs d'une instance de type structure peuvent être modifiés en modifiant l'emplacement de stockage dans lequel elle est détenue, et d'aucune autre manière. Il est possible de coder une structure de telle manière que la seule façon de muter n'importe quel champ est de construire une nouvelle instance entière et ensuite d'utiliser une assignation de struct pour muter tous les champs de la cible en les écrasant avec des valeurs de la nouvelle instance, mais à moins qu'une struct ne fournisse aucun moyen de créer une instance où ses champs ont des valeurs non-par défaut, tous ses champs seront mutables si et si la struct elle-même est stockée dans un emplacement mutable.

notez qu'il est possible de concevoir un type de structure de sorte qu'il se comporte essentiellement comme un type de classe, si la structure contient un champ de type de classe privé, et redirige ses propres membres à celui de l'objet de classe enveloppé. Par exemple , un PersonCollection pourrait offrir des propriétés SortedByName et SortedById , qui contiennent toutes deux une référence" immuable "à un PersonCollection (défini dans leur constructeur) et implémentent GetEnumerator en appelant soit creator.GetNameSortedEnumerator ou creator.GetIdSortedEnumerator . De telles structures se comporteraient un peu comme une référence à un PersonCollection , sauf que leurs méthodes GetEnumerator seraient liées à différentes méthodes dans le PersonCollection . On pourrait aussi avoir une enveloppe de structure a partie d'un tableau (par exemple , on pourrait définir une structure ArrayRange<T> qui contiendrait un T[] appelé Arr , un int Offset , et un int Length , avec une propriété indexée qui , pour un index idx dans la gamme de 0 à Length-1 , accéderait à Arr[idx+Offset] ). Malheureusement, si foo est une instance en lecture seule d'une telle structure, les versions actuelles des compilateurs n'autoriseront pas des opérations comme foo[3]+=4; parce qu'elles n'ont aucun moyen de déterminer si de telles opérations tenteraient d'écrire aux champs de foo .

il est également possible de concevoir une structure pour se comporter comme un type de valeur qui détient une collection de taille variable (qui apparaîtra être copiée chaque fois que la structure est), mais la seule façon de faire ce travail est de s'assurer qu'aucun objet auquel la structure détient une référence ne sera jamais exposé à quoi que ce soit qui pourrait la muter. Par exemple, on peut avoir une structure de type tableau qui contient un tableau, et dont la méthode indexée "put" crée un nouveau tableau dont le contenu est similaire à celui de l'original à l'exception d'un élément modifié. Malheureusement, il peut être quelque peu difficile de faire fonctionner de telles structures efficacement. Bien qu'il y ait des moments où la sémantique de la structure peut être pratique (par exemple, pouvoir passer une collection de type tableau à une routine, avec l'appelant et l'appelant sachant tous les deux que le code extérieur ne modifiera pas la collection, peut être mieux que d'exiger à la fois appelant et appelé recopie défensivement toutes les données qui leur sont données), l'exigence que les références de classe pointent vers des objets qui ne seront jamais mutés est souvent une contrainte assez sévère.

9
répondu supercat 2012-09-17 15:23:18

Nah - Je ne suis pas entièrement d'accord avec les règles. Ce sont de bonnes lignes directrices à considérer avec la performance et la standardisation, mais pas à la lumière des possibilités.

Comme vous pouvez le voir dans les réponses, il y a un journal des façons créatives de les utiliser. Donc, ces directives doivent être simplement que, toujours par souci de performance et d'efficacité.

dans ce cas, j'utilise des classes pour représenter des objets du monde réel dans leur forme plus grande, j'utilise des structures pour représenter les petits objets qui ont plus exacte utilise. La façon dont tu l'as dit, " un ensemble plus cohérent."Le mot-clé étant cohérent. Les classes seront davantage orientées objet, tandis que les structures peuvent avoir certaines de ces caractéristiques, leur sur une plus petite échelle. OMI.

je les utilise beaucoup dans Treeview et Listview tags où les attributs statiques communs peuvent être accédés très rapidement. Je lutte pour obtenir cette information d'une autre manière. Par exemple, dans Mes applications de base de données, je utilisez Treeview où j'ai des Tables, des SPs, des fonctions, ou tout autre objet. Je crée et peupler ma structure, la mets dans la balise, la sors, récupère les données de la sélection et ainsi de suite. Je ne ferais pas ça avec un cours!

j'essaie de les garder petits, de les utiliser dans des situations d'instance simple, et de les empêcher de changer. Il est prudent d'être conscient de la mémoire, de la répartition et de la performance. Et les tests sont tellement nécessaires.

8
répondu SnapJag 2009-02-06 20:20:51

Un struct est un type valeur. Si vous affectez une structure à une variable, la nouvelle variable contiendra une copie de l'original.

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

suppression des résultats suivants dans 5 instances de la structure stockée en mémoire:

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.

Un classe est un type de référence. Lorsque vous affectez une catégorie à une nouvelle variable, la variable contient une référence à l' classe d'origine de l'objet.

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

suppression des résultats suivants dans une seule fois de l'objet de classe en mémoire.

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 peut augmenter la probabilité d'une erreur de code. Si un objet value est traité comme un objet de référence mutable, un développeur peut être surpris lorsque des modifications apportées sont perdues de manière inattendue.

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
8
répondu Jason Williams 2017-07-20 18:36:18

une classe est un type de référence. Lorsqu'un objet de la classe est créée, la variable à laquelle l'objet est affecté contient uniquement une référence à la mémoire. Lorsque la référence d'objet est assignée à une nouvelle variable, la nouvelle variable se réfère à l'objet original. Les changements apportés par l'entremise d'une variable sont reflétés dans l'autre variable parce qu'ils se rapportent tous deux aux mêmes données. Une struct est un type de valeur. Quand une structure est créée, la variable à laquelle la structure est attribué détient le structure de données réelles. Lorsque la structure est assignée à une nouvelle variable, elle est copiée. La nouvelle variable et la variable originale contiennent donc deux copies distinctes des mêmes données. Les modifications apportées à une copie n'affectent pas l'autre copie. En général, les classes sont utilisées pour modéliser un comportement plus complexe, ou des données qui sont destinées à être modifiées après la création d'un objet de classe. Les structures sont les mieux adaptées pour les petites structures de données qui contiennent principalement des données qui ne sont pas destinées à être modifiées après la struct est créé.

les Classes et les Structures (Guide de Programmation C#)

7
répondu J_hajian_nzd 2014-05-23 09:52:52

j'ai fait un petit benchmark avec BenchmarkDotNet pour obtenir une meilleure compréhension de" struct " avantage en nombre. Je teste la boucle à travers le tableau (ou la liste) des structures (ou des classes). La création de ces tableaux ou de ces listes est hors de la portée du benchmark - il est clair que la "classe" est plus lourde utilisera plus de mémoire, et impliquera GC.

donc la conclusion est: Soyez prudent avec LINQ et les structures cachées boxe/Unbox et l'utilisation de structures pour les microoptimisations restent strictement dans les matrices.

P. S. Un autre point de référence sur le passage de struct / class à travers la pile d'appels est là 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 string Text1;
            public string Text2;
            public string Text3;
        }

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

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

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

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

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

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

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

        [Benchmark]
        public int TestLinqStruct()
        {
            var x = testListStruct.Select(i => i.Text1.Length + i.Text3.Length).Sum();
            return x;
        }
    }
7
répondu Roman Pokrovskij 2018-07-04 20:42:45

ma règle est

1, Toujours utiliser la classe;

2, S'il y a un problème de performance, j'essaie de changer une classe pour structurer en fonction des règles que @IAbstract a mentionnées, et ensuite faire un test pour voir si ces changements peuvent améliorer la performance.

6
répondu rockXrock 2013-08-30 08:22:20

je pense qu'une bonne première approximation est "jamais".

je pense qu'une bonne seconde approximation est "jamais".

si vous êtes désespéré pour perf, considérez-les, mais alors mesurez toujours.

4
répondu Brian 2009-02-06 17:44:34

Je m'occupais juste de Windows Communication Foundation [WCF] nommé Pipe et j'ai remarqué qu'il ne fait pas de sens d'utiliser des structures afin de s'assurer que l'échange de données est de type de valeur au lieu de type de référence .

4
répondu N_E 2017-04-27 15:54:04
La structure

peut être utilisée pour améliorer le rendement de la collecte des ordures. Bien que vous n'ayez Habituellement pas à vous soucier du rendement du GC, il y a des scénarios où cela peut être un tueur. Comme les grandes caches dans les applications à faible latence. Voir cet article pour un exemple:

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

2
répondu Rabbit 2013-07-03 14:20:39

les types de Structure ou de valeur peuvent être utilisés dans les scénarios suivants -

  1. si vous voulez empêcher que l'objet soit ramassé par une collecte des ordures.
  2. S'il s'agit d'un type simple et qu'aucune fonction membre ne modifie ses champs d'instance
  3. S'il n'est pas nécessaire de dériver d'autres types ou d'être dérivé d'autres types.

vous pouvez en savoir plus sur les types de valeur et les valeurs types ici sur ce lien

2
répondu Vikram 2015-08-31 08:50:01

brièvement, utiliser struct si:

1 - Vos propriétés/champs n'ont pas besoin d'être changés. Je veux dire que tu veux juste leur donner une valeur initiale et ensuite les lire.

2-les propriétés et les champs de votre objet sont de type valeur et ils ne sont pas si grands.

si c'est le cas, vous pouvez profiter des structures pour une meilleure performance et une allocation de mémoire optimisée car ils utilisent seulement des piles plutôt que des deux piles et tas (en classe)

2
répondu akazemis 2016-05-26 14:31:11

la structure c# est une alternative légère à une classe. Il peut faire presque la même chose qu'une classe, mais il est moins "coûteux" d'utiliser une structure plutôt qu'une classe. La raison en est un peu technique, mais pour résumer, les nouvelles instances d'une classe sont placées sur le tas, où les structures nouvellement instanciées sont placées sur la pile. De plus, vous ne traitez pas des références aux structures, comme avec les classes, mais vous travaillez directement avec l'instance struct. Cela signifie également que lorsque vous passez une structure à une fonction, c'est par la valeur, et non comme une référence. Il y a plus à ce sujet dans le chapitre sur les paramètres de fonction.

ainsi, vous devez utiliser des structures lorsque vous souhaitez représenter des structures de données plus simples, et surtout si vous savez que vous en instancierez beaucoup. Il y a beaucoup d'exemples dans le framework .NET, où Microsoft a utilisé des structures au lieu de classes, par exemple le Point, le Rectangle et la couleur struct.

2
répondu Saeed Dini 2017-10-24 13:15:22

j'utilise rarement une structure pour des choses. Mais c'est juste moi. Cela dépend si j'ai besoin que l'objet soit nul ou non.

comme indiqué dans d'autres réponses, j'utilise des classes pour les objets du monde réel. J'ai aussi l'état d'esprit des structures sont utilisées pour stocker de petites quantités de données.

1
répondu Flexo 2012-03-02 18:28:29

Il me semble que struct ont pas de fortes sémantiques qui donnent à l'utilisateur une idée forte de quand l'utiliser.

il ressemble comme une classe mais lac la plupart de sa caractéristique. C'est une sorte de version dégradée d'une classe. Il ya beaucoup de choses à propos de ne pas l'utiliser, mais très peu sur quand l'utiliser.

de l'OMI, il n'ya aucune raison pourquoi struct devrait être mis en œuvre dans un langage OO à la première place. En fait le type primitif ne devrait pas exister dans un oo pur langue, mais je m'égare.

c'est peut-être un moyen d'optimiser les choses. Une sorte de boxe libre choses qui peuvent être optimisés sur un certain site d'appel.

Mon 2 cents, je dirais qu'il a violé le baiser du principe de la langue et l'éviter autant que je peux.

-8
répondu mathk 2014-10-15 17:27:58

Structures sont à la plupart des égards comme des classes/objets. Structure peut contenir des fonctions, des membres et peut être hérité. Mais les structures sont en C # utilisé seulement pour données de tenue . Les Structures ne prendre moins de RAM que les classes et sont plus facile pour le garbage collector pour collecter . Mais quand vous utilisez des fonctions dans votre structure, alors compilateur prend en fait cette structure très similaire en classe / objet, donc si vous voulez quelque chose avec les fonctions , utilisez la classe/l'objet .

-10
répondu Cryo Erger 2014-11-20 20:02:07