web-dev-qa-db-fra.com

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

Un gars m'a posé cette question il y a quelques mois et je ne pouvais pas 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 que les types de référence sont delegate, interface, etc.

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

85
tugberk

Vos exemples sont un peu bizarres car int, bool et float sont des types spécifiques, les interfaces et les délégués sont genres de type - tout comme struct et enum sont des types de types 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 que vous trouvez déroutants.

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

Par exemple, imaginez une variable comme un morceau de papier. Il pourrait avoir la valeur "5" ou "false" écrit dessus, mais il ne pourrait pas avoir ma maison ... il faudrait que directions se rende chez moi. Ces instructions sont l’équivalent d’une référence. En particulier, deux personnes pourraient avoir différentes feuilles de papier contenant les mêmes instructions pour aller chez moi - et si une personne suivait ces instructions et peignait ma maison en rouge, la seconde personne verrait également ce changement. S'ils avaient tous les deux images distinctes de ma maison sur le papier, une personne coloriant leur papier ne changerait pas du tout le papier de l'autre personne.

151
Jon Skeet

Type de valeur:

Contient des valeurs, pas des adresses de mémoire

Exemple:

Struct

Espace de rangement:

TL; DR: la valeur d'une variable est stockée partout où elle est annulée. Les variables locales résident par exemple dans la pile, mais lorsqu'elles sont déclarées dans une classe en tant que membre, elles vivent sur le tas étroitement associé à la classe dans laquelle elle est déclarée. 
Longer: Ainsi, les types de valeur sont stockés partout où ils sont déclarés . Exemple: une valeur de int dans une fonction en tant que variable locale serait stockée dans la pile, tandis qu'une valeur de int est déclarée en tant que membre d'une classe, il serait stocké sur le segment de mémoire avec la classe dans laquelle il est déclaré. Un type de valeur sur une classe a un type de vie exactement identique à celui dans lequel elle est déclarée, ne nécessitant quasiment aucun travail du ramasse-miettes. C'est plus compliqué cependant, je ferais référence au livre de @ JonSkeet " C # In Depth " ou à son article " Memory in .NET " pour une explication plus concise.

Avantages:

Un type de valeur n'a pas besoin d'une récupération de place supplémentaire. Il récupère les ordures collectées avec l'instance dans laquelle il réside. Les variables locales des méthodes sont nettoyées lors de leur départ.

Désavantages:

  1. Lorsque de larges ensembles de valeurs sont passés à une méthode, la variable réceptrice copie en réalité, de sorte qu'il y a deux valeurs redondantes en mémoire.

  2. Comme les classes sont manquées, toutes les pertes sont avantageuses.

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 transmettez une variable de référence à une méthode et que celle-ci change, la valeur d'origine est modifiée alors que dans les types valeur, une copie de la variable donnée est prise et cette valeur est modifiée.

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

  3. Comme les classes sont des variables de type référence, elles offrent une possibilité de réutilisation, ce qui profite à 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 garbage collector

19
Durai Amuthan.H

J'ai trouvé qu'il était plus facile de comprendre la différence entre les deux si vous savez comment un ordinateur attribue des éléments en mémoire et 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 réellement une autre adresse mémoire de l'objet réel dans un emplacement mémoire différent.

L'exemple que je vais donner est grossièrement simplifié, prenez-le donc avec un grain de sel.

La mémoire d'ordinateur Imagine est un groupe de boîtes postales (commençant par les boîtes postales 0001 à n ° boîtes postales) pouvant 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. allouez de la mémoire (en commençant par l'emplacement de mémoire 1000 pour 5 octets) et mettez H (en 1000), e (en 1001), l (en 1002), l (en 1003) et o (en 1004). 
  2. allouer quelque part en mémoire (par exemple à l'emplacement 0500) et l'affecter comme variable a.
    Donc, c'est un peu comme un alias (0500 est un).
  3. assignez la valeur de cet emplacement mémoire (0500) à 1000 (c'est là que la chaîne Hello commence en mémoire). Ainsi, la variable a contient un référence à l'emplacement de mémoire réel de départ de la chaîne "Hello". 

Le type de valeur tiendra la chose réelle 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, dit à 0500, et l'affecter à la variable a (le même pseudonyme)
  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 détient réellement la valeur réelle et c'est pourquoi on l'appelle type valeur.
12
Jimmy Chandra

Ceci est tiré d'un de mes messages d'un forum différent, il y a environ deux ans. Bien que le langage utilisé soit vb.net (par opposition à C #), les concepts Type de valeur et Type de référence sont uniformes dans tout le .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 de 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 en 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 fait ce qui suit:

  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. Placez 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 de manière discrète dans chaque emplacement de 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 un ensemble d'allocations de mémoire de taille fixe?). Ils sont stockés dans le "tas géré". Les pointeurs (ou "références") vers chaque élément du segment de mémoire 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 segment de mémoire géré. Ainsi, lorsque votre code utilise une variable de référence, il utilise en fait un pointeur (ou une "adresse" vers un emplacement de mémoire dans le segment de mémoire géré). 

Supposons que vous ayez créé une classe nommée clsPerson, avec une chaîne de caractères Propriété Person.Name

Dans ce cas, lorsque vous faites une déclaration telle que 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 le souhaiteriez. La propriété p2.Name retournera ÉGALEMENT "Jim Morrison", comme on pourrait s'y attendre. Je crois que p1 et p2 représentent des adresses distinctes sur la pile. Toutefois, maintenant que vous avez affecté à p2 la valeur de p1, p1 et p2 désignent tous deux le même emplacement sur le segment de mémoire 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 personnes sur le segment de mémoire géré avec un pointeur p1 sur la pile référençant l'objet et affecté à nouveau à la propriété Nom de l'instance de l'objet une valeur de "Jim Morrison". Ensuite, vous avez créé un autre pointeur p2 dans la pile et vous l'avez pointé à la même adresse sur le segment de mémoire 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 à la fois par p1 et p2, de sorte que, si vous exécutez 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 tous les deux renvoyé "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 mettre au clair.). -EDIT: Je crois que c’est la raison pour laquelle vous définiriez p2 = rien OR 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 msgBoxes vont maintenant renvoyer "Jimi Hendrix"

Cela peut être assez déroutant pendant un moment, et je dirai une dernière fois que certains détails sont peut-être erronés. 

Bonne chance, et j'espère que d'autres personnes qui savent mieux que moi viendront nous aider à clarifier certaines choses. . . 

8
XIVSolutions

type de données 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) dans value (chaque variable a sa propre copie) mais
dans référence (plus que variable peut faire référence à certains objets)

3) dans value (la variable d'opération ne peut pas affecter d'autres variables) mais dans référence (variable peut affecter autre)

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

3
Mohamed Elmasry

"Les variables basées sur des types de valeur contiennent directement des valeurs. L'attribution d'une variable de type à 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 même." de la bibliothèque de Microsoft.

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

1
Lucas S.

Parfois, les explications ne seront pas utiles, surtout pour les débutants. Vous pouvez imaginer le type de valeur en tant que fichier de données et le type de référence en tant que raccourci vers un fichier.

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

1
Nime Cloud

Il n'y a pas une seule différence entre les types de valeur et les types de référence, il existe de nombreux petits détails qui sont explicitement énoncés dans la norme et certains d'entre eux ne sont pas faciles à comprendre, en particulier pour les débutants.

Voir ECMA standard 33, Common Language Infrastructure (CLI). La CLI est également normalisée par l'ISO. Je fournirais une référence, mais pour 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 de valeur peuvent être encadrés mais que les types de référence ne peuvent généralement pas l'être. Il y a des exceptions mais elles sont assez techniques.

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

0
user34660

Ceci est probablement faux de manière ésotérique, mais, pour simplifier les choses:

Les types de valeur sont des valeurs passées normalement "par valeur" (donc en les copiant). Les types de référence sont passés "par référence" (donnant ainsi un pointeur sur la valeur d'origine). Le standard .NET ECMA ne garantit pas où ces "éléments" sont enregistrés. Vous pouvez créer une implémentation de .NET sans pile ou sans empilement (la seconde serait très complexe, mais vous pourriez probablement utiliser des fibres et de nombreuses piles).

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

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

Maintenant .. Au final, vous avez le type de valeur, les "objets référencés" et les références (en C++, ils seraient appelés des pointeurs sur 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" à l'objet). Ces derniers sont similaires aux types de valeur (ils sont transmis par copie). Ainsi, un objet est composé de l'objet (un type de référence) et de zéro ou plusieurs références à celui-ci (qui sont similaires aux types de 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 Value 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, s'ils 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 (utilisant le nouveau CTP Async) ...). La valeur référencée ne peut aller qu'au tas. Les références utilisent les mêmes règles que les types de valeur.

Dans les cas de type de valeur qui vont sur le tas parce qu'ils sont dans une fonction itérateur, asynchrone ou référencée 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 construite lorsque vous appelez la fonction.

Maintenant, je ne sais pas écrire de longues choses et j'ai mieux à 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 il s'agit d'un article condensé "prêt à l'emploi".

0
xanatos

Supposons que v soit une expression/variable de type valeur et que r soit une 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 référence de type varaible stocke uniquement un lien vers une boîte métaphorique où se trouve la valeur. 

0
Anas Elghafari

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 concernant la quantité de "type valeur", je commenterai la manière dont cela est géré sur la plate-forme. NET, en particulier en C # (CSharp), lorsqu'il est appelé APIS, envoie des paramètres par valeur, par référence, dans nos méthodes et fonctions, ainsi que la manière de traiter correctement les passages de ces valeurs.

Lire cet articleValeur de type de variable et référence en C #

0
Marcelo Cavalini

Type de valeur:  

  • Taille de la mémoire fixe.

  • Stocké dans la mémoire de pile.

  • Détient la valeur réelle.

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

Type de référence:

  • Mémoire non fixée.

  • Stocké dans la mémoire de tas.

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

    Ex. string, array, class, etc ...

0
Dhinagaran P

En termes simples, les types de valeur sont passés par leur valeur et les types de référence par leur référence (adresse mémoire). 

Cela signifie que les modifications apportées aux paramètres de type valeur (les paramètres formels) dans une méthode appelée ne seront pas répercutées dans les valeurs à partir desquelles la méthode a été appelée (les paramètres réels).

Cependant, les modifications apportées aux paramètres de référence dans une méthode appelée refléteront les modifications apportées aux variables déclarées dans la méthode appelante.

C'est une brève explication. Reportez-vous à ici pour comprendre en détail les types de valeur, les types de référence et le type de valeur par rapport au type de référence.

0
Ranganatha

La manière la plus simple de penser aux types de référence est de les considérer comme des "identificateurs 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, d’enquêter ou de manipuler le type, ou d’en comparer deux pour l’égalité. Toute tentative de faire autre chose avec un identifiant d'objet sera considérée comme un raccourci pour effectuer l'action indiquée avec l'objet désigné par cet identifiant.

Supposons que j'ai deux variables X et Y de type Car - un type de référence. Y arrive à contenir "objet ID # 19531". Si je dis "X = Y", cela fera en sorte que X conserve "l'objet ID # 19531". Notez que ni X ni Y ne tiennent une voiture. La voiture, également appelée "objet ID # 19531", est stockée ailleurs. Quand j'ai copié Y dans X, tout ce que j'ai fait était de 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 trouver "l'objet ID # 19531" et le peindre en bleu. Notez que même si X et Y font maintenant 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 font référence à "l'objet ID # 19531", qui est toujours la même voiture a toujours été.

0
supercat