Quelle est la différence entre un type de référence et un type de valeur en c #?

100

Un type m'a posé cette question il y a quelques mois et je n'ai pas pu l'expliquer en détail. Quelle est la différence entre un type de référence et un type de valeur en C #?

Je sais que les types de valeur sont int, bool, float, etc et les types référence sont delegate, interfaceetc. Ou est - ce mal aussi?

Pouvez-vous me l'expliquer de manière professionnelle?

remorqueur
la source
3
En guise de petite note, je pense que la question est posée sur C #, mais en réalité, il s'agit de C # + .NET. Vous ne pouvez pas analyser C # sans analyser .NET. Je ne retaguerai pas la question car il pourrait y avoir des points à faire sur l'analyse de l'un sans analyser l'autre (itérateurs et fermetures, je vous regarde)
xanatos
@xanatos, c'est une question sur la CLI que C #, VB.Net et Net ont tous en commun. Il devrait y avoir une balise pour CLI mais CLI est prise pour autre chose. Il y a CLR mais c'est une implémentation, pas une norme.
user34660

Réponses:

172

Vos exemples sont un peu étranges parce que tandis que int, boolet floatsont des types spécifiques, les interfaces et les délégués sont des types de types - tout comme structet enumsont des types de valeur.

J'ai écrit une explication des types de référence et des types de valeur dans cet article . Je serais heureux de développer tous les éléments qui vous semblent déroutants.

La version "TL; DR" consiste à penser à la valeur d'une variable / expression d'un type particulier. Pour un type valeur, la valeur est l'information elle-même. Pour un type référence, la valeur est une référence qui peut être nulle ou peut être un moyen de naviguer vers un objet contenant les informations.

Par exemple, considérez une variable comme un morceau de papier. Il pourrait avoir la valeur "5" ou "false" écrit dessus, mais il ne pouvait pas avoir ma maison ... il faudrait qu'il y ait des indications pour ma maison. Ces directions sont l'équivalent d'une référence. En particulier, deux personnes pourraient avoir des morceaux de papier différents contenant les mêmes directions vers ma maison - et si une personne suivait ces instructions et peignait ma maison en rouge, la deuxième personne verrait également ce changement. S'ils avaient tous les deux des photos séparées de ma maison sur le papier, alors une personne coloriant son papier ne changerait pas du tout le papier de l'autre personne.

Jon Skeet
la source
2
Il est important de noter qu'il existe trois types de sémantiques primaires distincts qu'une chose peut offrir: la sémantique immuable, la sémantique de valeur mutable et la sémantique de référence mutable. Conceptuellement, le type de sémantique qu'une chose implémente est orthogonale selon qu'elle est stockée en tant qu'objet de tas autonome ou en tant que variable / champ (struct). En pratique, alors que les structures qui n'exposent pas leurs champs peuvent implémenter n'importe quel type de sémantique, le fait que .net autorise le partage promiscuité des références de tas signifie que les objets de tas ne peuvent pas implémenter une sémantique de valeur mutable.
supercat du
Je n'ai pas compris ce morceau - while int, bool and float are specific types, interfaces and delegates are kinds of type - just like struct and enum are kinds of value types. Qu'entendez-vous par int, bool étant des types spécifiques? Tout en C #, par exemple int, bool, float, classe, interface, délégué est un type (type de données pour être précis). Les types de données sont séparés en tant que «Type de référence» et «Type de valeur» en C #. Alors pourquoi dites-vous que int est un type spécifique mais que l'interface est une sorte de type?
RBT
2
@RBT: Les types de données ne sont pas seulement séparés en «type de référence» et «type de valeur». Ils sont également séparés en "classe, struct, enum, délégué, interface". intest une structure, stringest une classe, Actionest un délégué, etc. Votre liste de "int, bool, float, class, interface, delegate" est une liste contenant différents types de choses, de la même manière que "10, int" est une liste contenant différents types de choses.
Jon Skeet
@JonSkeet La réponse sur ce post est peut-être un peu trompeuse alors.
RBT
@RBT: Je dirais que c'est un peu mal formulé, mais pas terrible.
Jon Skeet
26

Type de valeur:

Contient une certaine valeur et non des adresses mémoire

Exemple:

Struct

Espace de rangement:

TL; DR : la valeur d'une variable est stockée là où elle est décleared. Les variables locales vivent sur la pile par exemple, mais lorsqu'elles sont déclarées à l'intérieur d'une classe en tant que membre, elles vivent sur le tas étroitement couplé avec la classe dans laquelle elles sont déclarées.
Plus long : Ainsi, les types de valeur sont stockés partout où ils sont déclarés. Exemple: une intvaleur à l'intérieur d'une fonction en tant que variable locale serait stockée sur la pile, tandis qu'une intvaleur in déclarée en tant que membre d'une classe serait stockée sur le tas avec la classe dans laquelle elle est déclarée. Un type de valeur sur une classe a un type de vie qui est exactement le même que la classe dans laquelle elle est déclarée, ne nécessitant pratiquement aucun travail de la part du garbage collector. C'est plus compliqué cependant, je ferais référence au livre de @ JonSkeet " C # In Depth "Mémoire en .NET"pour une explication plus concise.

Avantages:

Un type valeur n'a pas besoin de garbage collection supplémentaire. Il récupère les déchets avec l'instance dans laquelle il réside. Les variables locales dans les méthodes sont nettoyées lors du départ de la méthode.

Désavantages:

  1. Lorsqu'un grand ensemble de valeurs est passé à une méthode, la variable réceptrice est effectivement copiée, de sorte qu'il y a deux valeurs redondantes en mémoire.

  2. Comme les cours sont manqués, il perd tous les avantages oop

Type de référence:

Contient une adresse mémoire d'une valeur et non d'une valeur

Exemple:

Classe

Espace de rangement:

Stocké sur le tas

Avantages:

  1. Lorsque vous passez une variable de référence à une méthode et que cela change, cela change effectivement la valeur d'origine, alors que dans les types valeur, une copie de la variable donnée est prise et sa valeur est modifiée.

  2. Lorsque la taille de la variable est plus grande, le type de référence est bon

  3. Les classes étant des variables de type référence, elles donnent une réutilisabilité, bénéficiant ainsi à la programmation orientée objet

Désavantages:

Plus de travail de référencement lors de l'allocation et des déréférences lors de la lecture de la surcharge value.extra pour le garbage collector

Durai Amuthan.H
la source
5
Il n'est pas nécessairement vrai que les types de référence sont stockés sur le tas et que les types de valeur sont stockés sur la pile. Lisez yoda.arachsys.com/csharp/memory.html si vous souhaitez en savoir plus.
Rhys
1
Il y a beaucoup de malentendus dans cette réponse. Veuillez lire Jeff Richters CLR via C #. Les types de valeur sont stockés sur la pile de threads et ne sont pas soumis au garbage collection (GC) - ils n'ont rien à voir avec GC. Les types de référence sont stockés sur le tas géré et sont donc soumis à GC. Si un type de référence a une référence racine, il ne peut pas être collecté et est promu aux générations, 0, 1 et 2. S'il n'a pas de référence racine, il peut être Garbage Collected et il passe ensuite par ce processus appelé Résurrection où il est tué et ramené à la vie puis finalement recueilli
Jeremy Thompson
13

J'ai trouvé plus facile de comprendre la différence des deux si vous savez comment l'ordinateur alloue des éléments en mémoire et savez ce qu'est un pointeur.

La référence est généralement associée à un pointeur. Cela signifie que l'adresse mémoire où réside votre variable contient en fait une autre adresse mémoire de l'objet réel dans un emplacement mémoire différent.

L'exemple que je vais donner est excessivement simplifié, alors prenez-le avec un grain de sel.

Imaginez que la mémoire de l'ordinateur est un ensemble de boîtes postales consécutives (commençant par la boîte postale 0001 à la boîte postale n) qui peut contenir quelque chose à l'intérieur. Si les boîtes postales ne le font pas pour vous, essayez une table de hachage ou un dictionnaire ou un tableau ou quelque chose de similaire.

Ainsi, lorsque vous faites quelque chose comme:

var a = "Bonjour";

l'ordinateur effectuera les opérations suivantes:

  1. allouer de la mémoire (disons en commençant à l'emplacement de mémoire 1000 pour 5 octets) et mettre H (à 1000), e (à 1001), l (à 1002), l (à 1003) et o (à 1004).
  2. allouer quelque part en mémoire (par exemple à l'emplacement 0500) et l'affecter comme variable a.
    C'est donc un peu comme un alias (0500 est a).
  3. affectez la valeur à cet emplacement de mémoire (0500) à 1000 (où la chaîne Hello commence en mémoire). Ainsi, la variable a contient une référence à l'emplacement de mémoire de départ réel de la chaîne "Hello".

Le type de valeur contiendra l'objet réel dans son emplacement de mémoire.

Ainsi, lorsque vous faites quelque chose comme:

var a = 1;

l'ordinateur effectuera les opérations suivantes:

  1. allouer un emplacement mémoire disons à 0500 et l'assigner à la variable a (le même alias)
  2. mettez-y la valeur 1 (à l'emplacement de mémoire 0500).
    Notez que nous n'allouons pas de mémoire supplémentaire pour contenir la valeur réelle (1). Ainsi, a contient en fait la valeur réelle et c'est pourquoi on l'appelle type valeur.
Jimmy Chandra
la source
1
Vous pourriez être intéressé par blogs.msdn.com/b/ericlippert/archive/2009/02/17/…
Jon Skeet
@Jon, Eh bien, cela invalide ce que je disais, LOL. Mais comme je l'ai dit, il est extrêmement simplifié d'obtenir une certaine compréhension entre les deux types, ce que j'ai trouvé utile dans mon cas. Au moins c'est comme ça que je l'ai imaginé dans mon esprit :).
Jimmy Chandra le
8

Ceci est tiré d'un de mes messages provenant d'un autre forum, il y a environ deux ans. Alors que le langage est vb.net (par opposition à C #), les concepts de type valeur vs référence sont uniformes dans tout .net et les exemples sont toujours valables.

Il est également important de se rappeler que dans .net, TOUS les types dérivent techniquement du type de base Object. Les types valeur sont conçus pour se comporter comme tels, mais à la fin, ils héritent également des fonctionnalités du type de base Object.

A. Les types de valeur ne sont que cela - ils représentent une zone distincte de la mémoire où une valeur discrète est stockée. Les types de valeur ont une taille de mémoire fixe et sont stockés dans la pile, qui est une collection d'adresses de taille fixe.

Lorsque vous faites une déclaration comme celle-ci:

Dim A as Integer
DIm B as Integer

A = 3
B = A 

Vous avez effectué les opérations suivantes:

  1. Création de 2 espaces en mémoire suffisants pour contenir des valeurs entières de 32 bits.
  2. Placé une valeur de 3 dans l'allocation de mémoire attribuée à A
  3. Placé une valeur de 3 dans l'allocation de mémoire attribuée à B en lui attribuant la même valeur que celle détenue dans A.

La valeur de chaque variable existe discrètement dans chaque emplacement mémoire.

B. Les types de référence peuvent être de différentes tailles. Par conséquent, ils ne peuvent pas être stockés dans la "pile" (rappelez-vous, la pile est une collection d'allocations de mémoire de taille fixe?). Ils sont stockés dans le "Managed Heap". Les pointeurs (ou «références») vers chaque élément du tas géré sont conservés dans la pile (comme une adresse). Votre code utilise ces pointeurs dans la pile pour accéder aux objets stockés dans le tas managé. Ainsi, lorsque votre code utilise une variable de référence, il utilise en fait un pointeur (ou une "adresse" vers un emplacement mémoire dans le tas géré).

Supposons que vous ayez créé une classe nommée clsPerson, avec une chaîne Property Person.Name

Dans ce cas, lorsque vous faites une déclaration comme celle-ci:

Dim p1 As clsPerson
p1 = New clsPerson
p1.Name = "Jim Morrison"

Dim p2 As Person

p2 = p1

Dans le cas ci-dessus, la propriété p1.Name renverra "Jim Morrison", comme vous vous en doutez. La propriété p2.Name renverra AUSSI "Jim Morrison", comme vous vous en doutez intuitivement. Je crois que p1 et p2 représentent des adresses distinctes sur la pile. Cependant, maintenant que vous avez attribué à p2 la valeur de p1, p1 et p2 pointent vers le MÊME EMPLACEMENT sur le tas géré.

Maintenant, considérez cette situation:

Dim p1 As clsPerson
Dim p2 As clsPerson

p1 = New clsPerson
p1.Name = "Jim Morrison"

p2 = p1

p2.Name = "Janis Joplin"

Dans ce cas, vous avez créé une nouvelle instance de la classe de personne sur le tas géré avec un pointeur p1 sur la pile qui fait référence à l'objet et attribué à nouveau à la propriété Name de l'instance d'objet la valeur «Jim Morrison». Ensuite, vous avez créé un autre pointeur p2 dans la pile et l'avez pointé sur la même adresse sur le tas géré que celle référencée par p1 (lorsque vous avez effectué l'affectation p2 = p1).

Voici la torsion. Lorsque vous attribuez à la propriété Name de p2 la valeur "Janis Joplin", vous modifiez la propriété Name de l'objet REFERENCED par les deux p1 et p2, de sorte que, si vous avez exécuté le code suivant:

MsgBox(P1.Name)
'Will return "Janis Joplin"

MsgBox(p2.Name)
'will ALSO return "Janis Joplin"Because both variables (Pointers on the Stack) reference the SAME OBJECT in memory (an Address on the Managed Heap). 

Cela avait-il du sens?

Dernier. Si tu fais ça:

DIm p1 As New clsPerson
Dim p2 As New clsPerson

p1.Name = "Jim Morrison"
p2.Name = "Janis Joplin"

Vous avez maintenant deux objets Personne distincts. Cependant, à la minute où vous recommencez:

p2 = p1

Vous avez maintenant renvoyé les deux vers "Jim Morrison". (Je ne sais pas exactement ce qui est arrivé à l'objet sur le tas référencé par p2... JE PENSE qu'il est maintenant hors de portée. C'est l'un de ces domaines où, espérons-le, quelqu'un peut me redresser...). -EDIT: JE CROIS que c'est pourquoi vous définiriez p2 = Nothing OU p2 = New clsPerson avant d'effectuer la nouvelle affectation.

Encore une fois, si vous faites maintenant ceci:

p2.Name = "Jimi Hendrix"

MsgBox(p1.Name)
MsgBox(p2.Name)

Les deux msgBox renverront désormais "Jimi Hendrix"

Cela peut être assez déroutant pendant un moment, et je dirai une dernière fois, il se peut que certains détails soient faux.

Bonne chance, et j'espère que d'autres personnes qui en savent mieux que moi viendront vous aider à clarifier certains de ces points. . .

XIVSolutions
la source
Je ne sais pas pourquoi vous n'avez pas reçu de votes positifs. Bonne réponse, m'a aidé à comprendre avec des exemples clairs et simples.
Harry
Quant aux concepts de type valeur et de type référence , ils sont uniformes dans tout le .net, ils sont en fait définis dans la spécification Common Language Infrastructure (CLI), la norme Ecma 335 (également une norme ISO). C'est la norme pour la partie standard de .Net. La norme Ecma 334 (également une norme ISO) est le langage C # et il indique explicitement que les implémentations C # doivent soit s'appuyer sur la CLI, soit prendre en charge une autre façon d'obtenir les fonctionnalités CLI minimales requises par cette norme C # . Cependant, VB.Net n'est pas un standard, il est propriétaire de Microsoft.
user34660
5

type de données de valeur et type de données de référence

1) valeur (contient les données directement) mais référence (fait référence aux données)

2) en valeur (chaque variable a sa propre copie) mais
en référence (plus de variable peut faire référence à certains objets)

3) en valeur (la variable d'opération ne peut pas avoir d'effet sur une autre variable) mais en référence (la variable peut affecter l'autre)

4) les types de valeur sont (int, bool, float) mais le type de référence est (tableau, objets de classe, chaîne)

Mohamed Elmasry
la source
2

Type de valeur:

  • Taille de la mémoire fixe.

  • Stocké dans la mémoire de la pile.

  • Contient la valeur réelle.

    Ex. int, char, bool, etc ...

Type de référence:

  • Mémoire non fixe.

  • Stocké dans la mémoire du tas.

  • Contient l'adresse mémoire de la valeur réelle.

    Ex. chaîne, tableau, classe, etc ...

Dhinagaran P
la source
1

"Les variables basées sur des types de valeur contiennent directement des valeurs. L'affectation d'une variable de type valeur à une autre copie la valeur contenue. Cela diffère de l'affectation de variables de type référence, qui copie une référence à l'objet mais pas à l'objet lui-même." à partir de la bibliothèque de Microsoft.

Vous pouvez trouver une réponse plus complète ici et ici .

Lucas S.
la source
1
Je n'aime pas cette explication, car il semble que l'affectation fonctionne différemment pour les types de référence et les types de valeur. Ce n'est pas le cas. Dans les deux cas, cela rend la valeur de la variable "cible" égale à l'expression - la valeur est copiée. La différence réside dans la nature de cette valeur - pour les types référence, la valeur copiée est une référence. C'est toujours la valeur de la variable.
Jon Skeet
Je suis d'accord avec vous et j'ai déjà su que cela pouvait être différent, comme vous pouvez le lire dans cet article . Mais, je ne fais que repasser le guide de Microsoft sur le sujet et aussi la façon dont vous lisez habituellement dans les livres. Ne me blâmez pas! :)
Lucas S.
Oh bien sûr ... il y a beaucoup de bits de documentation MSDN où il y a des fautes à trouver :)
Jon Skeet
1

Parfois, les explications n'aideront pas particulièrement pour les débutants. Vous pouvez imaginer le type de valeur comme fichier de données et le type de référence comme raccourci vers un fichier.

Donc, si vous copiez une variable de référence, vous ne copiez que le lien / pointeur vers une donnée réelle quelque part en mémoire. Si vous copiez un type valeur, vous clonez réellement les données en mémoire.

Nime Cloud
la source
0

C'est probablement faux de manière ésotérique, mais, pour faire simple:

Les types de valeur sont des valeurs qui sont transmises normalement "par valeur" (donc en les copiant). Les types de référence sont passés "par référence" (donnant ainsi un pointeur vers la valeur d'origine). Il n'y a aucune garantie par la norme .NET ECMA de l'endroit où ces «choses» sont enregistrées. Vous pouvez créer une implémentation de .NET sans pile ou sans pile (la seconde serait très complexe, mais vous pourriez probablement, en utilisant des fibres et de nombreuses piles)

Les structures sont de type valeur (int, bool ... sont des structures, ou du moins sont simulées comme ...), les classes sont de type référence.

Les types valeur descendent de System.ValueType. Le type de référence descend de System.Object.

Maintenant ... À la fin, vous avez le type de valeur, les «objets référencés» et les références (en C ++, ils seraient appelés pointeurs vers des objets. En .NET, ils sont opaques. Nous ne savons pas ce qu'ils sont. De notre point de vue, ils sont des «poignées» de l'objet). Ces derniers sont similaires aux types de valeur (ils sont passés par copie). Ainsi, un objet est composé par l'objet (un type de référence) et zéro ou plusieurs références à lui (qui sont similaires aux types valeur). Lorsqu'il n'y a aucune référence, le GC la collectera probablement.

En général (dans l'implémentation "par défaut" de .NET), le type Valeur peut aller sur la pile (s'il s'agit de champs locaux) ou sur le tas (s'il s'agit de champs d'une classe, si ce sont des variables dans une fonction itérateur, si ce sont des variables référencées par une fermeture, si elles sont variables dans une fonction asynchrone (en utilisant le nouveau CTP Async) ...). La valeur référencée ne peut aller que dans le tas. Les références utilisent les mêmes règles que les types Valeur.

Dans les cas de type de valeur qui vont sur le tas parce qu'ils sont dans une fonction d'itérateur, une fonction asynchrone ou sont référencés par une fermeture, si vous regardez le fichier compilé, vous verrez que le compilateur a créé une classe pour mettre ces variables et la classe est créée lorsque vous appelez la fonction.

Maintenant, je ne sais pas écrire de longues choses et j'ai de meilleures choses à faire dans ma vie. Si vous voulez une version "précise" "académique" "correcte", lisez CECI:

http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

Ça fait 15 minutes que je le cherche! C'est mieux que les versions msdn, car c'est un article condensé "prêt à l'emploi".

xanatos
la source
1
C'est faux de manière plus qu'ésotérique. C'est fondamentalement faux je dirais - parce que les valeurs de type de référence sont toujours passées par valeur aussi; c'est juste que la valeur est une référence, pas un objet. Voir pobox.com/~skeet/csharp/parameters.html . Oh, et les variables locales peuvent également se retrouver sur le tas, par exemple si elles sont capturées ou font partie d'un bloc d'itérateur.
Jon Skeet
Les blocs d'itérateurs sont convertis en classes, donc "derrière vous" ce sont des "champs d'une classe". Idem pour les fermetures. Ouais ... j'ai oublié d'écrire la distinction entre le "pointeur" (la référence) et le "pointé"
xanatos
@xanatos: Bien sûr, ce sont des champs d'une classe après compilation - mais ce sont toujours des variables locales dans le code source. Je n'appellerais pas non plus les références elles-mêmes «types de valeur» - je pense que je sais d'où vous venez, mais je ne pense pas que ce soit une bonne idée de brouiller les eaux de cette façon.
Jon Skeet
@jon Ouais ... Ils sont un troisième type, car les pointeurs sont "opaques" dans .net, et ils ne dérivent pas de ValueType. Mais ils ressemblent plus aux types valeur qu'aux références. Vous pouvez les "ref" et "les sortir". J'ai dû brouiller les eaux parce que "quelqu'un" devait pinailler le fonctionnement des itérateurs.
xanatos le
En regardant l'article sur lequel je pointe maintenant, j'ai trouvé: "Il existe trois types de valeurs: (1) instances de types valeur, (2) instances de types référence et (3) références. (Le code en C # ne peut pas manipuler des instances de types référence directement; il le fait toujours via une référence. Dans le code unsafe, les types pointeurs sont traités comme des types valeur afin de déterminer les besoins de stockage de leurs valeurs. ) ".
xanatos
0

La façon la plus simple de penser aux types de référence est de les considérer comme des "ID d'objet"; les seules choses que l'on puisse faire avec un ID d'objet sont d'en créer un, d'en copier un, de rechercher ou de manipuler le type d'un, ou de comparer deux pour l'égalité. Une tentative de faire quoi que ce soit d'autre avec un ID d'objet sera considérée comme un raccourci pour faire l'action indiquée avec l'objet référencé par cet ID.

Supposons que j'ai deux variables X et Y de type Car - un type de référence. Y contient "ID d'objet # 19531". Si je dis "X = Y", cela amènera X à tenir "ID d'objet # 19531". Notez que ni X ni Y ne possèdent de voiture. La voiture, également appelée "ID d'objet # 19531", est stockée ailleurs. Quand j'ai copié Y dans X, je n'ai fait que copier le numéro d'identification. Supposons maintenant que je dise X.Color = Colors.Blue. Une telle déclaration sera considérée comme une instruction d'aller chercher "ID d'objet # 19531" et de le peindre en bleu. Notez que même si X et Y font désormais référence à une voiture bleue plutôt qu'à une voiture jaune, la déclaration n'affecte pas réellement X ou Y, car les deux se réfèrent toujours à «ID d'objet # 19531», qui est toujours la même voiture que celle-ci a toujours été.

supercat
la source
0

Les types de variables et la valeur de référence sont faciles à appliquer et bien appliqués au modèle de domaine, facilitent le processus de développement.

Pour supprimer tout mythe autour de la quantité de "type valeur", je vais commenter la façon dont cela est géré sur la plate-forme. NET, spécifiquement en C # (CSharp) lorsqu'il est appelé APIS et envoie des paramètres par valeur, par référence, dans nos méthodes, et fonctions et comment faire le traitement correct des passages de ces valeurs.

Lisez cet article Valeur de type variable et référence en C #

Marcelo Cavalini
la source
Ceci est un site de questions / réponses en anglais uniquement, malheureusement = \. Merci d'avoir essayé de répondre, cependant. Veuillez créer des réponses complètes, avec des liens à titre d'aide uniquement (mais pas comme réponse complète et soutenue). Veuillez regarder comment répondre .
Jesse
0

Supposons que ce vsoit une expression / variable de type valeur, et rune expression / variable de type référence

    x = v  
    update(v)  //x will not change value. x stores the old value of v

    x = r 
    update(r)  //x now refers to the updated r. x only stored a link to r, 
               //and r can change but the link to it doesn't .

Ainsi, une variable de type valeur stocke la valeur réelle (5 ou "h"). Une variable de type référence ne stocke qu'un lien vers une boîte métaphorique où se trouve la valeur.

Anas Elghafari
la source
0

Avant d'expliquer les différents types de données disponibles en C #, il est important de mentionner que C # est un langage fortement typé. Cela signifie que chaque variable, constante, paramètre d'entrée, type de retour et en général chaque expression qui évalue une valeur, a un type.

Chaque type contient des informations qui seront incorporées par le compilateur dans le fichier exécutable en tant que métadonnées qui seront utilisées par le Common Language Runtime (CLR) pour garantir la sécurité du type lorsqu'il alloue et récupère de la mémoire.

Si vous voulez savoir combien de mémoire un type spécifique alloue, vous pouvez utiliser l'opérateur sizeof comme suit:

static void Main()
{
    var size = sizeof(int);
    Console.WriteLine($"int size:{size}");
    size = sizeof(bool);
    Console.WriteLine($"bool size:{size}");
    size = sizeof(double);
    Console.WriteLine($"double size:{size}");
    size = sizeof(char);
    Console.WriteLine($"char size:{size}");
}

La sortie affichera le nombre d'octets alloués par chaque variable.

int size:4
bool size:1
double size:8
char size:2

Les informations relatives à chaque type sont:

  • L'espace de stockage requis.
  • Les valeurs maximum et minimum. Par exemple, le type Int32 accepte des valeurs comprises entre 2147483648 et 2147483647.
  • Le type de base dont il hérite.
  • Emplacement où la mémoire des variables sera allouée au moment de l'exécution.
  • Les types d'opérations autorisées.
  • Les membres (méthodes, champs, événements, etc.) contenus par le type. Par exemple, si nous vérifions la définition du type int, nous trouverons la structure et les membres suivants:

    namespace System
    {
        [ComVisible(true)]
        public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>
        {      
            public const Int32 MaxValue = 2147483647;     
            public const Int32 MinValue = -2147483648;
            public static Int32 Parse(string s, NumberStyles style, IFormatProvider provider);    
            ... 
        }  
    }

Gestion de la mémoire Lorsque plusieurs processus s'exécutent sur un système d'exploitation et que la quantité de RAM n'est pas suffisante pour tout contenir, le système d'exploitation mappe des parties du disque dur avec la RAM et commence à stocker les données sur le disque dur. Le système d'exploitation utilisera des tables spécifiques où les adresses virtuelles sont mappées à leurs adresses physiques correspondantes pour exécuter la demande. Cette capacité de gestion de la mémoire est appelée mémoire virtuelle.

Dans chaque processus, la mémoire virtuelle disponible est organisée dans les 6 sections suivantes mais pour la pertinence de ce sujet, nous nous concentrerons uniquement sur la pile et le tas.

Pile La pile est une structure de données LIFO (dernier entré, premier sorti), dont la taille dépend du système d'exploitation (par défaut, pour les machines ARM, x86 et x64, Windows réserve 1 Mo, tandis que Linux réserve de 2 Mo à 8 Mo selon le version).

Cette partie de la mémoire est gérée automatiquement par la CPU. Chaque fois qu'une fonction déclare une nouvelle variable, le compilateur alloue un nouveau bloc de mémoire aussi grand que sa taille sur la pile, et lorsque la fonction est terminée, le bloc de mémoire de la variable est désalloué.

Heap Cette région de mémoire n'est pas gérée automatiquement par le CPU et sa taille est plus grande que la pile. Lorsque le nouveau mot clé est appelé, le compilateur commence à rechercher le premier bloc de mémoire libre qui correspond à la taille de la demande. et quand il le trouve, il est marqué comme réservé en utilisant la fonction C intégrée malloc () et renvoie le pointeur vers cet emplacement. Il est également possible de désallouer un bloc de mémoire en utilisant la fonction C intégrée free (). Ce mécanisme provoque une fragmentation de la mémoire et doit utiliser des pointeurs pour accéder au bon bloc de mémoire, il est plus lent que la pile pour effectuer les opérations de lecture / écriture.

Types personnalisés et intégrés intégrés Alors que C # fournit un ensemble standard de types intégrés représentant des entiers, des booléens, des caractères de texte, etc., vous pouvez utiliser des constructions telles que struct, class, interface et enum pour créer vos propres types.

Un exemple de type personnalisé utilisant la construction struct est:

struct Point
{
    public int X;
    public int Y;
};

Types de valeur et de référence Nous pouvons classer le type C # dans les catégories suivantes:

  • Types de valeur
  • Types de référence

Types de valeur Les types de valeur dérivent de la classe System.ValueType et les variables de ce type contiennent leurs valeurs dans leur allocation de mémoire dans la pile. Les deux catégories de types valeur sont struct et enum.

L'exemple suivant montre le membre de type boolean. Comme vous pouvez le voir, il n'y a pas de référence explicite à la classe System.ValueType, cela se produit car cette classe est héritée par la structure.

namespace System
{
    [ComVisible(true)]
    public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean>
    {
        public static readonly string TrueString;
        public static readonly string FalseString;
        public static Boolean Parse(string value);
        ...
    }
}

Types de référence D'autre part, les types de référence ne contiennent pas les données réelles stockées dans une variable, mais l'adresse mémoire du tas où la valeur est stockée. Les catégories de types de référence sont les classes, les délégués, les tableaux et les interfaces.

Lors de l'exécution, lorsqu'une variable de type référence est déclarée, elle contient la valeur null jusqu'à ce qu'un objet qui a été créé à l'aide des mots-clés new lui soit affecté.

L'exemple suivant montre les membres du type générique List.

namespace System.Collections.Generic
{
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(Generic.Mscorlib_CollectionDebugView<>))]
    [DefaultMember("Item")]
    public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
    {
        ...
        public T this[int index] { get; set; }
        public int Count { get; }
        public int Capacity { get; set; }
        public void Add(T item);
        public void AddRange(IEnumerable<T> collection);
        ...
    }
}

Au cas où vous souhaiteriez connaître l'adresse mémoire d'un objet spécifique, la classe System.Runtime.InteropServices fournit un moyen d'accéder aux objets gérés à partir de la mémoire non gérée. Dans l'exemple suivant, nous allons utiliser la méthode statique GCHandle.Alloc () pour allouer un handle à une chaîne, puis la méthode AddrOfPinnedObject pour récupérer son adresse.

string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");

La sortie sera

Memory address:39723832

Références Documentation officielle: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019

Ivan Porta
la source
-1

Il existe de nombreux petits détails sur les différences entre les types de valeur et les types de référence qui sont explicitement énoncés par la norme et certains d'entre eux ne sont pas faciles à comprendre, en particulier pour les débutants.

Voir la norme ECMA 33, Common Language Infrastructure (CLI) . La CLI est également normalisée par l'ISO. Je fournirais une référence mais pour l'ECMA, nous devons télécharger un PDF et ce lien dépend du numéro de version. Les normes ISO coûtent de l'argent.

Une différence est que les types valeur peuvent être encadrés mais que les types référence ne peuvent généralement pas l'être. Il y a des exceptions mais elles sont assez techniques.

Les types valeur ne peuvent pas avoir de constructeurs d'instances ou de finaliseurs sans paramètre et ils ne peuvent pas faire référence à eux-mêmes. Se référer à eux-mêmes signifie par exemple que s'il existe une valeur de type Node, alors un membre de Node ne peut pas être un Node . Je pense qu'il y a d'autres exigences / limitations dans les spécifications, mais si c'est le cas, elles ne sont pas rassemblées en un seul endroit.

utilisateur34660
la source