[c#] Quand utiliser struct?



Answers

Chaque fois que vous n'avez pas besoin de polymorphisme, recherchez la valeur sémantique et souhaitez éviter l'allocation de tas et le surcoût de la récupération de place associée. La mise en garde, cependant, est que les structures (arbitrairement grandes) sont plus chères à 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 en pratique.

Question

Quand devriez-vous utiliser struct et non class en C #? Mon modèle conceptuel est que les structs sont utilisés dans les moments où l'article est simplement une collection de types de valeur . Une façon de les loger tous ensemble dans un ensemble cohérent.

Je suis tombé sur ces règles here :

  • Une structure devrait représenter une seule valeur.
  • 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.

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




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



Voici une règle de base.

  • Si tous les champs membres sont des types de 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.

Exemples

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
}



À l'exception des types de valeurs utilisés directement par le moteur d'exécution et divers autres pour les besoins de PInvoke, vous ne devez utiliser les types de valeurs que 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.



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 downvoting ceci mais c'est un point valide, et a été fait avant que l'op clarifie sa question, et c'est la raison fondamentale la plus fondamentale pour une structure.

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




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 rarely use a struct for things. Mais c'est juste moi. 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.




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

Edit: Commentaire indique que vous pouvez utiliser class ou struct avec StructLayoutAttribute et c'est certainement vrai. En pratique, vous utiliserez généralement une structure - elle est allouée sur la pile par rapport au tas, ce qui est logique si vous passez simplement un argument à un appel de méthode non géré.




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 fonction, mais contrairement aux classes, les structures sont des types de valeur et ne nécessitent pas d'allocation de tas. Une variable d'un type struct stocke directement les données de la structure, tandis qu'une variable d'un type classe stocke une référence à un objet alloué dynamiquement. Les types de structure ne prennent pas en charge l'héritage spécifié par l'utilisateur et tous les types de structure héritent implicitement de l'objet type.

Les structures sont particulièrement utiles pour les structures de données de petite taille ayant 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 structs plutôt que de classes pour les structures de données de petite taille 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 objets 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 est de faire du point une structure.

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 du tableau) et les instances Point sont stockées en ligne dans le tableau.

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

Avec les classes, il est possible que deux variables référencent le même objet et donc que les opérations sur une variable affectent l'objet référencé par l'autre variable. Avec les structs, les variables ont chacune leur propre copie des données, et il n'est pas possible que les opérations sur l'une affectent l'autre. Par exemple, la sortie produite par le fragment de code suivant dépend 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 Point est une classe, la sortie est 20 car a et b font référence au même objet. 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. Premièrement, la copie d'une structure entière est généralement moins efficace que la copie d'une référence d'objet, de sorte que l'attribution et le passage de paramètre de valeur peuvent être plus coûteux avec des structures qu'avec des 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 aux structures, ce qui exclut leur utilisation dans un certain nombre de situations.







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.




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)




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<T> 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 compiler 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.




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 compiler actually takes that structure very similarly as class/object, so if you want something with functions, then use class/object .






Related



Tags

c# c#   struct