Brug af ref, in og out
I C# er der tre nøgleord, der bruges til at kontrollere, hvordan argumenter overføres til metoder: in
, out
og ref
. Disse nøgleord ændrer metodekaldets semantik og giver forskellige grader af adgang til variabler i metoder.
Information til undervisere
Man skal have noget erfaring for at forstå nødvendigheden af ref
, in
og out
. Men det er en god idé at introducere disse nøgleord tidligt, så eleverne kan blive fortrolige med dem, inden de støder på dem i kode fra andre. Understreg, at det er en god idé at bruge disse nøgleord med forsigtighed, da de kan gøre koden mere kompleks og svær at læse, og at det typisk kun handler om mikro optimering.
ref
I C# har du mulighed for at bruge kodeordet ref
til at arbejde med referencer til variabler. Dette kan være nyttigt, når du vil sende en reference til en variabel til en metode, så metoden kan ændre værdien af variablen. Det komplicerer koden noget og kan gøre den sværere at læse og vedligeholde, men til gengæld kan det giver nogle performance forbedringer. Derfor bør du kun bruge ref
, når det er nødvendigt.
Der er flere måder at bruge ref
på, her er et par eksempler.
Brug af ref
i metodekald
void Increment1(int number)
{
number++;
}
void Increment2(ref int number)
{
number++;
}
int value = 5;
Increment1(value);
Console.WriteLine(value); // Output: 5
Increment2(ref value);
Console.WriteLine(value); // Output: 6
I dette eksempel bruges ref
til at sende en reference til en variabel til metoden Increment
. Metoden øger værdien af variablen med 1, og ændringerne er synlige uden for metoden. Hvis du fjerner ref
-nøgleordet, vil metoden modtage en kopi af variablen, og ændringerne vil ikke blive synlige uden for metoden. Bemærk, at du skal bruge ref
både i metodekaldet og i metode signaturen.
Brug af ref
i returnerede værdier
SomeClass someClass = new SomeClass();
someClass.PrintArray(); // 1 2 3
ref int element = ref someClass.GetArrayElement(1);
element = 42;
someClass.PrintArray(); // 1 42 3
ref int saveRef = ref element;
saveRef = 99;
someClass.PrintArray(); // 1 99 3
class SomeClass
{
private int[] array = { 1, 2, 3 };
public ref int GetArrayElement(int index)
{
return ref array[index];
}
public void PrintArray()
{
Console.WriteLine(string.Join(" ", array));
}
}
Her bruges ref
til at returnere en reference til et arrayelement fra en metode. Dette gør det muligt at ændre værdien af arrayelementet direkte fra metodekaldet. Det minder lidt om brugen af Span<T>
og Memory<T>
, som også giver mulighed for at arbejde med hukommelsesblokke på en mere effektiv måde.
Brug af ref til reference typer
Ved reference baserede argumenter kan man ikke ikke rette referencen (eksempelvis pege på et nyt objekt), men ref
giver mulighed for helt at ændre referencen:
class Person
{
public string Name { get; set; }
}
void Test1(Person p)
{
// der er overført en kopi af p
// så hvis man tildeler p en ny
// reference har det ikke nogen
// effekt i den kaldende metode
p = new Person(); // ingen betydning
}
void Test2(ref Person p)
{
// der er overført den originale reference
// så hvis man tildeler p en ny reference har
// det betydning i den kaldende metode
p = new Person(); // betydning
}
I dette eksempel er der to metoder, Test1
og Test2
, der tager en Person
som argument. I Test1
er der ikke brugt ref
, så metoden modtager en kopi af Person
-objektet. Hvis du tildeler p
en ny reference, vil det ikke have nogen effekt uden for metoden. I Test2
er der brugt ref
, så metoden modtager den originale reference til Person
-objektet. Hvis du tildeler p
en ny reference, vil det have betydning uden for metoden.
out
Brugen er out
er meget lig ref
bortset fra, at out
ikke kræver at variablen er initialiseret før metoden kaldes, og at metoden skal tildele en værdi til variablen før metoden returnerer.
static void GetMinMax(int[] numbers, out int min, out int max)
{
min = numbers[0];
max = numbers[0];
foreach (int number in numbers)
{
if (number < min) min = number;
if (number > max) max = number;
}
}
Metoden kan kaldes med
int min, max; // bemærk - behøver ikke initialiseres
int[] a = { 1, 2, 3 };
GetMinMax(a, out min, out max);
in
Når et argument mærkes med in
nøgleordet, betyder det, at værdier ikke kopieres og at metoden kun kan læse værdien af argumentet og ikke ændre den. Dette er særligt nyttigt for at optimere ydeevnen, når der arbejdes med store værdityper, da det forhindrer unødvendige kopiering af data.
public struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
public static double CalculateDistance(in Point p1, in Point p2)
{
int deltaX = p1.X - p2.X;
int deltaY = p1.Y - p2.Y;
return Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
}
Info
Bemærk at in-kodeordet er såkaldt micro optimering, men det kan give en fordel ved store værdibaserede typer i et i forvejen belastet system.
Det virker også med reference baserede variabler med giver begrænset effekt:
I dette eksempel er in nøgleordet ikke nødvendigt, fordi det ikke giver ydeevnegevinster for reference-typer. Men det giver stadig en beskyttelse mod utilsigtet ændring af parameteren, selvom det er mere symbolsk i dette tilfælde.