Question

Quelle est la différence entre une structure et une classe dans .NET ?

Était-ce utile?

La solution

Dans .NET, il existe deux catégories de types, types de référence et types de valeur.

Les structures sont types de valeur et les cours sont types de référence.

La différence générale est qu'un Type de référence vit sur le tas, et un type de valeur vit en ligne, c'est-à-dire partout où votre variable ou votre champ est défini.

Une variable contenant un type de valeur contient l'intégralité type de valeur 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 un référence vers un autre endroit de la mémoire où réside la valeur réelle.

Cela présente un avantage, pour commencer :

  • types de valeur contient toujours une valeur
  • types de référence peut contenir un nul-référence, ce qui signifie qu'ils ne font référence à rien du tout pour le moment

Intérieurement, Type de référenceLes s sont implémentés comme des pointeurs, et sachant cela et sachant comment fonctionne l'affectation des variables, il existe d'autres modèles de comportement :

  • copier le contenu d'un type de valeur variable dans une autre variable, copie tout le contenu dans la nouvelle variable, rendant les deux distinctes.En d'autres termes, après la copie, les modifications apportées à l'un n'affecteront pas l'autre.
  • copier le contenu d'un Type de référence variable dans une autre variable, copie la référence, ce qui signifie que vous avez maintenant deux références à la même ailleurs stockage des données réelles.En d'autres termes, après la copie, la modification des données dans une référence semblera également affecter l'autre, mais uniquement parce que vous regardez simplement les mêmes données aux deux endroits.

Lorsque vous déclarez des variables ou des champs, voici en quoi les deux types diffèrent :

  • variable: type de valeur vit sur la pile, Type de référence vit sur la pile comme un pointeur vers un endroit de la mémoire du tas où se trouve la mémoire réelle (bien que notez Série d'articles d'Eric Lipperts :La pile est un détail d'implémentation.)
  • classe/champ-struct : type de valeur vit complètement à l'intérieur du type, Type de référence vit à l'intérieur du type comme pointeur vers un endroit de la mémoire de tas où se trouve la mémoire réelle.

Autres conseils

Un bref résumé de chacun :

Cours uniquement :

  • Peut prendre en charge l'héritage
  • Sont des types de référence (pointeur)
  • La référence peut être nulle
  • Avoir une surcharge de mémoire par nouvelle instance

Structures uniquement :

  • Ne peut pas prendre en charge l'héritage
  • Sont des types de valeur
  • Sont transmis par valeur (comme des entiers)
  • Ne peut pas avoir de référence nulle (sauf si Nullable est utilisé)
  • Ne pas avoir de surcharge de mémoire par nouvelle instance - sauf si "en boîte"

Classes et structures :

  • Les types de données composés sont-ils généralement utilisés pour contenir quelques variables ayant une relation logique
  • Peut contenir des méthodes et des événements
  • Peut prendre en charge les interfaces

Dans .NET, les déclarations de structure et de classe font la différence entre les types référence et les types valeur.

Lorsque vous transmettez un type de référence, il n’y en a qu’un seul réellement stocké.Tout le code qui accède à l’instance accède à la même.

Lorsque vous transmettez 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 un cours, 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 rien être – 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 ».

De Microsoft Choisir entre classe et structure ...

En règle générale, la majorité des types dans un cadre devraient être des classes.Il existe cependant certaines situations dans lesquelles les caractéristiques d'un type de valeur le rendent plus approprié pour utiliser des structures.

CONSIDÉRER 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 intégrées dans d'autres objets.

X ÉVITER une structure sauf si le type a tous des caractéristiques suivantes:

  • Il représente logiquement une valeur unique, similaire aux types primitifs (int, double, etc.).
  • Sa taille d'instance est inférieure à 16 octets.
  • C'est immuable. (ne peut pas être modifié)
  • Il ne sera pas nécessaire de le mettre en boîte fréquemment.

En plus de toutes les différences décrites dans les autres réponses :

  1. Structures ne peut pas avoir de constructeur explicite sans paramètre alors qu'une classe peut
  2. Structures ne peut pas avoir de destructeurs, alors qu'une classe peut
  3. Structures je ne peux pas hériter d'une autre structure ou classe alors qu'une classe peut hériter d'une autre classe.(Les structures et les classes peuvent être implémentées à partir d'une interface.)

Si vous recherchez une vidéo expliquant toutes les différences, vous pouvez consulter 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 de l’objet lui-même.

Les structures (techniquement, les types 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 faire appel à un constructeur de copie personnalisé.Passer un type de valeur à une méthode implique de copier la valeur entière, encore une fois sans appeler de code personnalisable.

La distinction est meilleure grâce aux noms C++/CLI :"classe ref" est une classe telle que décrite en premier, "classe de valeur" est une classe telle que décrite en second.Les mots-clés « class » et « struct » utilisés par C# sont simplement quelque chose qui doit être appris.

Différence entre les structures et les classes :

  • Les structures sont de type valeur alors que Les classes sont de type référence.
  • Les structures sont stockées sur la pile alors que Les cours sont stockés sur le tas.
  • Les types de valeur maintiennent leur valeur en mémoire où ils sont déclarés, mais le type de référence contient une référence à une mémoire d'objet.
  • Types de valeur détruits immédiatement Une fois la portée perdue, tandis que le type de référence ne détruit que la variable après la perte de la portée.L'objet est plus tard détruit par le collecteur des ordures.
  • Lorsque vous copiez la structure dans une autre structure, une nouvelle copie de cette structure est créée modifiée d'une structure n'affectera pas la valeur de l'autre structure.
  • Lorsque vous copiez une classe dans une autre classe, il copie uniquement la variable de référence.
  • Les deux variables de référence pointent vers le même objet sur le tas.La modification d'une variable affectera l'autre variable de référence.
  • Les structures ne peuvent pas avoir de destructeurs, mais les classes peuvent avoir des destructeurs.
  • Les structures ne peuvent pas avoir de constructeurs explicites sans paramètre tandis qu'une classe peut ne pas prendre en charge l'héritage, mais les classes le font.Les deux prennent en charge l'héritage d'une interface.
  • Les structures sont de type scellé.

Structure vs classe

Une structure est un type valeur, elle est donc stockée sur la pile, mais une classe est un type référence et est stockée sur le tas.

Une structure ne prend pas en charge l'héritage ni 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 valeur, on ne peut pas attribuer null à un objet struct, mais ce n'est pas le cas pour une classe.

Juste pour être complet, 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

Donc, les structures conviennent aux objets de type numérique, comme les points (enregistrez les coordonnées x et y).Et les cours sont adaptés aux autres.Même si 2 personnes ont le même nom, taille, poids..., elles restent 2 personnes.

Pour compléter les autres réponses, il existe une différence fondamentale qui mérite d’être notée : la manière dont elle est stockée en mémoire.Cela peut avoir un effet majeur sur les performances des baies.Les structures sont des types valeur, elles stockent donc une valeur dans la zone de mémoire vers laquelle elles pointent, les classes sont des types référence, elles font donc référence à une classe dans la zone de mémoire vers laquelle elles pointent, la valeur réelle est stockée ailleurs.

  • Avec une structure, la mémoire est allouée dans la classe conteneur pour stocker les données.
  • Avec une classe, la classe contenante contiendra simplement un pointeur vers la nouvelle classe dans une zone différente de la mémoire.

Cela est également vrai avec les tableaux, donc un tableau de structures ressemble à ceci en mémoire

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

Où un tableau de classes ressemble à ceci

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

Les valeurs réelles qui vous intéressent ne sont pas réellement stockées dans le tableau, mais ailleurs en 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 du processeur.Utiliser des classes alors que vous auriez pu/devriez utiliser des structures augmentera considérablement le nombre de manques de cache sur le processeur.

La chose la plus lente qu'un processeur moderne fait n'est pas de traiter les chiffres, il récupère les données de la mémoire, et un accès au cache L1 est plusieurs fois plus rapide que la lecture des données de la RAM.

Eh bien, pour commencer, une structure est transmise par valeur plutôt que par référence.Les structures conviennent aux 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 fondamentale du spécificateur d'accès, et quelques-unes mentionnées ci-dessus, je voudrais ajouter certaines des différences majeures, y compris quelques-unes de celles 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 :

  • Sont des types valeur et ne nécessitent pas d’allocation de tas.
  • L'allocation de mémoire est différente et est stockée dans la pile
  • Utile pour les petites structures de données
  • Affecte les performances, lorsque nous transmettons une valeur à la méthode, nous transmettons toute la structure de données et tout est transmis à la pile.
  • Le constructeur renvoie simplement la valeur de la structure elle-même (généralement dans un emplacement temporaire sur la pile), et cette valeur est ensuite copiée si nécessaire
  • 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.
  • Ne prennent pas en charge l'héritage spécifié par l'utilisateur et héritent implicitement du type d'objet

Classe:

  • Valeur du type de référence
  • Stocké dans le tas
  • Stocker une référence à un objet alloué dynamiquement
  • Les constructeurs sont invoqués avec l'opérateur new, mais cela n'alloue pas de mémoire sur le tas
  • Plusieurs variables peuvent avoir une référence au même objet
  • Il est possible que les opérations sur une variable affectent l'objet référencé par l'autre variable.

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

Sortir

La valeur initiale de l'objet Struct est :dix

Méthode à l'intérieur de la structure Intérieur La valeur de la méthode de l'objet struct est:20

Après l'appel de méthode, la valeur de l'objet Struct est :dix

La valeur initiale de l'objet de classe est :dix

Méthode de classe à l'intérieur de la classe L'intermédiaire de la méthode de la valeur de l'objet de classe est:20

Après l'appel de méthode, la valeur de l'objet de classe est :20

Ici, vous pouvez clairement voir la différence entre l'appel par valeur et l'appel par référence.

  1. Les événements déclarés dans une classe ont leurs accès += et -= automatiquement verrouillés via un verrou (this) pour les rendre thread-safe (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 leurs accès += et -= automatiquement verrouillés.Un lock(this) pour une structure ne fonctionnerait pas puisque vous ne pouvez verrouiller que sur une expression de type référence.

  2. La création d'une instance de structure ne peut pas provoquer de garbage collection (sauf si le constructeur crée directement ou indirectement une instance de type référence), alors que la création d'une instance de type référence peut provoquer un garbage collection.

  3. 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 puisque tous ses constructeurs peuvent être privés.

    class NonInstantiable
    {
        private NonInstantiable() // OK
        {
        }
    }
    
    struct Direct
    {
        private Direct() // Compile-time error
        {
        }
    }
    
  4. Une structure ne peut pas avoir de destructeur.Un destructeur n'est qu'un remplacement d'objet. Finalisez déguisé, et les structures, étant des types 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
    
  5. Une structure est implicitement scellée, une classe ne l'est pas.
    Une structure ne peut pas être abstraite, une classe le 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 le peut.
    Une structure ne peut pas déclarer de membres protégés (par exemple, des champs, des types imbriqués), contrairement à une classe.
    Une structure ne peut pas déclarer de membres de fonctions abstraites, une classe abstraite le peut.
    Une structure ne peut pas déclarer de membres de 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 de membres de fonction de remplacement, une classe le peut.
    La seule exception à cette règle est qu'une structure peut remplacer les méthodes virtuelles de System.Object, à savoir Equals(), GetHashCode() et ToString().

Comme mentionné précédemment:Les classes sont de type référence tandis que les Structs sont des types valeur avec toutes les conséquences.

En guise de règle générale, les directives de conception du framework recommandent d'utiliser des structures au lieu de classes si :

  • Il a une taille d'instance inférieure à 16 octets
  • Il représente logiquement une valeur unique, similaire aux types primitifs (int, double, etc.)
  • C'est immuable
  • Il ne sera pas nécessaire de le mettre en boîte fréquemment
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+
|                       |                                                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                                                                        |
| Access Modifiers      | public, private, internal                                                                            | public, protected, internal, protected internal, private protected                                |
+-----------------------+------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------+

Les structures sont la valeur réelle : elles peuvent être vides mais jamais nulles.

Cela est vrai, mais notez également qu'à partir de .NET 2, les structures prennent en charge une version Nullable et que C# fournit du sucre syntaxique pour le rendre plus facile à utiliser.

int? value = null;
value  = 1;

Il existe un cas intéressant de casse-tête "classe vs struct" - situation dans laquelle vous devez renvoyer plusieurs résultats de la méthode :choisissez lequel utiliser.Si vous connaissez l'histoire de ValueTuple, vous savez que ValueTuple (struct) a été ajouté car il devrait être plus efficace que Tuple (class).Mais qu’est-ce que cela signifie en chiffres ?Deux épreuves :l'un est struct/class qui a 2 champs, l'autre avec struct/class qui a 8 champs (avec une dimension supérieure à 4 - la classe devrait devenir plus efficace que struct en termes de ticks de processeur, mais bien sûr, la charge GC doit également être prise en compte).

P.S.Une autre référence pour le cas spécifique « structure ou classe avec collections » est la suivante : 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 codes :

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

Chaque variable ou champ d'un type valeur primitif ou d'un type structure contient une instance unique de ce type, y compris tous ses champs (publics et privés).En revanche, les variables ou les champs de types référence peuvent contenir la valeur null ou faire référence à un objet, stocké ailleurs, auquel 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 être partie de un autre objet de 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, créant ainsi tous les champs de la manière par défaut.Créer un nouveau exemple d'un type de référence commencera par créer tous les champs de la manière par défaut, puis exécutera du code supplémentaire facultatif en fonction du type.

Copier une variable ou un champ d'un type primitif vers un autre copiera la valeur.Copier une variable ou un champ de type structure vers un autre copiera tous les champs (publics et privés) de la première instance vers la dernière instance.Copier une variable ou un champ de type référence vers un autre fera que ce dernier fera référence à la même instance que le premier (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 pour .NET.Si un type implémente une sémantique de valeur mutable, copier une variable de ce type dans une autre copie les propriétés de la première dans une autre instance, référencée par la seconde, et utiliser un membre de la seconde pour la muter entraînera la modification de cette deuxième instance. , mais pas le premier.Si un type implémente une sémantique de référence mutable, copier une variable dans une autre et utiliser un membre de la seconde pour muter l'objet affectera l'objet référencé par la première variable ;les types avec une sémantique immuable n'autorisent 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 valeur d’implémenter n’importe laquelle des sémantiques ci-dessus, à condition que tous leurs champs puissent faire de même.Cependant, un type référence ne peut implémenter qu'une sémantique de référence mutable ou une sémantique immuable ;les types 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top