C# Language
Waardetype versus referentietype
Zoeken…
Syntaxis
- Doorgeven door referentie: publieke nietig Double (ref int numberToDouble) {}
Opmerkingen
Invoering
Waardetypes
Waardetypes zijn eenvoudiger van de twee. Waardetypes worden vaak gebruikt om gegevens zelf weer te geven. Een geheel getal, een Boolean of een punt in 3D-ruimte zijn allemaal voorbeelden van goede waardetypen.
Waardetypes (structs) worden gedeclareerd met het sleutelwoord struct. Zie de syntaxis voor een voorbeeld van het declareren van een nieuwe struct.
Over het algemeen hebben we 2 zoekwoorden die worden gebruikt om waardetypes aan te geven:
- structs
- opsommingen
Referentietypen
Referentietypen zijn iets complexer. Referentietypen zijn traditionele objecten in de zin van Object Oriented Programming. Ze ondersteunen dus overerving (en de voordelen daarvan) en ondersteunen ook finalizers.
In C # hebben we over het algemeen deze referentietypen:
- Klassen
- afgevaardigden
- interfaces
Nieuwe referentietypen (klassen) worden gedeclareerd met het trefwoord class. Zie voor een voorbeeld het gedeelte Syntaxis voor het declareren van een nieuw referentietype.
Grote verschillen
De belangrijkste verschillen tussen referentietypen en waardetypen zijn hieronder te zien.
Waardetypes bestaan op de stapel, referentietypes bestaan op de heap
Dit is het vaak genoemde verschil tussen de twee, maar het komt er eigenlijk op neer dat wanneer je een waardetype in C # gebruikt, zoals een int, het programma die variabele gebruikt om rechtstreeks naar die waarde te verwijzen. Als je int mine = 0 zegt, verwijst de variabele mine rechtstreeks naar 0, wat efficiënt is. Referentietypen bevatten echter (zoals de naam al doet vermoeden) een verwijzing naar het onderliggende object, dit lijkt op verwijzingen in andere talen zoals C ++.
U zult de effecten hiervan misschien niet meteen opmerken, maar de effecten zijn er, zijn krachtig en zijn subtiel. Zie het voorbeeld over het wijzigen van referentietypes elders voor een voorbeeld.
Dit verschil is de belangrijkste reden voor de volgende andere verschillen en is het weten waard.
Waardetypes veranderen niet wanneer u ze in een methode wijzigt, referentietypes wel
Wanneer een waardetype als parameter in een methode wordt doorgegeven en de methode op een of andere manier de waarde wijzigt, wordt de waarde niet gewijzigd. Als een referentietype in dezelfde methode wordt opgenomen en wordt gewijzigd, wordt het onderliggende object gewijzigd, zodat andere dingen die hetzelfde object gebruiken, hebben het nieuw gewijzigde object in plaats van hun oorspronkelijke waarde.
Zie het voorbeeld van waardetypen versus referentietypen in methoden voor meer info.
Wat als ik ze wil veranderen?Voer ze eenvoudigweg in uw methode in met behulp van het trefwoord "ref" en u passeert dit object vervolgens door verwijzing. Dit betekent dat het hetzelfde object in het geheugen is. Wijzigingen die u aanbrengt, worden dus gerespecteerd. Zie het voorbeeld over het passeren van referentie voor een voorbeeld.
Waardetypes kunnen niet null zijn, referentietypes wel
Vrijwel zoals het zegt, je kunt null toewijzen aan een referentietype, wat betekent dat aan de variabele die je hebt toegewezen, geen object kan worden toegewezen. In het geval van waardetypes is dit echter niet mogelijk. Je kunt echter Nullable gebruiken om je waardetype nullable te laten zijn, als dit een vereiste is, maar als dit iets is dat je overweegt, denk er dan sterk over na of een klasse hier niet de beste aanpak is, als het je eigen is type.
Waarden elders wijzigen
public static void Main(string[] args)
{
var studentList = new List<Student>();
studentList.Add(new Student("Scott", "Nuke"));
studentList.Add(new Student("Vincent", "King"));
studentList.Add(new Student("Craig", "Bertt"));
// make a separate list to print out later
var printingList = studentList; // this is a new list object, but holding the same student objects inside it
// oops, we've noticed typos in the names, so we fix those
studentList[0].LastName = "Duke";
studentList[1].LastName = "Kong";
studentList[2].LastName = "Brett";
// okay, we now print the list
PrintPrintingList(printingList);
}
private static void PrintPrintingList(List<Student> students)
{
foreach (Student student in students)
{
Console.WriteLine(string.Format("{0} {1}", student.FirstName, student.LastName));
}
}
U zult merken dat hoewel de lijst printingList is gemaakt vóór de correcties in de namen van studenten na de typefouten, de methode PrintPrintingList de gecorrigeerde namen nog steeds afdrukt:
Scott Duke
Vincent Kong
Craig Brett
Dit komt omdat beide lijsten een lijst met verwijzingen naar dezelfde studenten bevatten. Dus het wijzigen van het onderliggende studentobject propogates in gebruik door beide lijsten.
Dit is hoe de studentenklasse eruit zou zien.
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Student(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
}
Doorgeven door referentie
Als u wilt dat de Waardetypes versus Referentietypen in het methodenvoorbeeld correct werken, gebruikt u het trefwoord ref in uw methodehandtekening voor de parameter die u als referentie wilt doorgeven, evenals wanneer u de methode aanroept.
public static void Main(string[] args)
{
...
DoubleNumber(ref number); // calling code
Console.WriteLine(number); // outputs 8
...
}
public void DoubleNumber(ref int number)
{
number += number;
}
Als u deze wijzigingen aanbrengt, wordt de nummerupdate zoals verwacht, wat betekent dat de console-uitvoer voor nummer 8 zou zijn.
Doorgeven via referentie met ref-trefwoord.
Uit de documentatie :
In C # kunnen argumenten worden doorgegeven aan parameters, hetzij door waarde, hetzij door verwijzing. Doorgeven door verwijzing stelt functieleden, methoden, eigenschappen, indexeerders, operatoren en constructeurs in staat om de waarde van de parameters te wijzigen en die wijziging in de aanroepomgeving te laten bestaan. Gebruik het trefwoord
ref
ofout
om een parameter door te geven.
Het verschil tussen ref
en out
is dat out
betekent dat de doorgegeven parameter moet worden toegewezen voordat de functie eindigt. In contrastparameters die worden doorgegeven met ref
kunnen worden gewijzigd of ongewijzigd worden gelaten.
using System;
class Program
{
static void Main(string[] args)
{
int a = 20;
Console.WriteLine("Inside Main - Before Callee: a = {0}", a);
Callee(a);
Console.WriteLine("Inside Main - After Callee: a = {0}", a);
Console.WriteLine("Inside Main - Before CalleeRef: a = {0}", a);
CalleeRef(ref a);
Console.WriteLine("Inside Main - After CalleeRef: a = {0}", a);
Console.WriteLine("Inside Main - Before CalleeOut: a = {0}", a);
CalleeOut(out a);
Console.WriteLine("Inside Main - After CalleeOut: a = {0}", a);
Console.ReadLine();
}
static void Callee(int a)
{
a = 5;
Console.WriteLine("Inside Callee a : {0}", a);
}
static void CalleeRef(ref int a)
{
a = 6;
Console.WriteLine("Inside CalleeRef a : {0}", a);
}
static void CalleeOut(out int a)
{
a = 7;
Console.WriteLine("Inside CalleeOut a : {0}", a);
}
}
Uitgang :
Inside Main - Before Callee: a = 20
Inside Callee a : 5
Inside Main - After Callee: a = 20
Inside Main - Before CalleeRef: a = 20
Inside CalleeRef a : 6
Inside Main - After CalleeRef: a = 6
Inside Main - Before CalleeOut: a = 6
Inside CalleeOut a : 7
Inside Main - After CalleeOut: a = 7
toewijzing
var a = new List<int>();
var b = a;
a.Add(5);
Console.WriteLine(a.Count); // prints 1
Console.WriteLine(b.Count); // prints 1 as well
Door aan een variabele van een List<int>
wordt geen kopie van de List<int>
. In plaats daarvan kopieert het de verwijzing naar de List<int>
. We noemen typen die zich op deze manier gedragen, referentietypen .
Verschil met methodeparameters ref en uit
Er zijn twee mogelijke manieren om een waardetype als referentie door te geven: ref
en out
. Het verschil is dat door deze met ref
door te geven, de waarde moet worden geïnitialiseerd, maar niet wanneer deze wordt doorgegeven out
. Gebruik out
zorgt ervoor dat de variabele een waarde na de methodeaanroep:
public void ByRef(ref int value)
{
Console.WriteLine(nameof(ByRef) + value);
value += 4;
Console.WriteLine(nameof(ByRef) + value);
}
public void ByOut(out int value)
{
value += 4 // CS0269: Use of unassigned out parameter `value'
Console.WriteLine(nameof(ByOut) + value); // CS0269: Use of unassigned out parameter `value'
value = 4;
Console.WriteLine(nameof(ByOut) + value);
}
public void TestOut()
{
int outValue1;
ByOut(out outValue1); // prints 4
int outValue2 = 10; // does not make any sense for out
ByOut(out outValue2); // prints 4
}
public void TestRef()
{
int refValue1;
ByRef(ref refValue1); // S0165 Use of unassigned local variable 'refValue'
int refValue2 = 0;
ByRef(ref refValue2); // prints 0 and 4
int refValue3 = 10;
ByRef(ref refValue3); // prints 10 and 14
}
De vangst is dat door gebruik te out
de parameter must
worden geïnitialiseerd voordat de methode wordt verlaten, daarom is de volgende methode mogelijk met ref
maar niet met out
:
public void EmtyRef(bool condition, ref int value)
{
if (condition)
{
value += 10;
}
}
public void EmtyOut(bool condition, out int value)
{
if (condition)
{
value = 10;
}
} //CS0177: The out parameter 'value' must be assigned before control leaves the current method
Dit komt omdat als de condition
niet van toepassing is, de value
wordt toegewezen.
ref vs out parameters
Code
class Program
{
static void Main(string[] args)
{
int a = 20;
Console.WriteLine("Inside Main - Before Callee: a = {0}", a);
Callee(a);
Console.WriteLine("Inside Main - After Callee: a = {0}", a);
Console.WriteLine();
Console.WriteLine("Inside Main - Before CalleeRef: a = {0}", a);
CalleeRef(ref a);
Console.WriteLine("Inside Main - After CalleeRef: a = {0}", a);
Console.WriteLine();
Console.WriteLine("Inside Main - Before CalleeOut: a = {0}", a);
CalleeOut(out a);
Console.WriteLine("Inside Main - After CalleeOut: a = {0}", a);
Console.ReadLine();
}
static void Callee(int a)
{
a += 5;
Console.WriteLine("Inside Callee a : {0}", a);
}
static void CalleeRef(ref int a)
{
a += 10;
Console.WriteLine("Inside CalleeRef a : {0}", a);
}
static void CalleeOut(out int a)
{
// can't use a+=15 since for this method 'a' is not intialized only declared in the method declaration
a = 25; //has to be initialized
Console.WriteLine("Inside CalleeOut a : {0}", a);
}
}
uitgang
Inside Main - Before Callee: a = 20
Inside Callee a : 25
Inside Main - After Callee: a = 20
Inside Main - Before CalleeRef: a = 20
Inside CalleeRef a : 30
Inside Main - After CalleeRef: a = 30
Inside Main - Before CalleeOut: a = 30
Inside CalleeOut a : 25
Inside Main - After CalleeOut: a = 25