Programmerare, skeptiker, sekulärhumanist, antirasist.
Författare till bok om C64 och senbliven lantis.
Röstar pirat.
2023-04-13
Om inget annat anges skickas parametrar till funktioner “by value” i C#. Det innebär att den mottagande funktionen får en egen kopia av värdet som skickas. Att skicka “by reference” (nyckelordet ref) skiljer sig därmed, eftersom funktionen får en referens till ursprungsvariabeln. För en referensvariabel (som myValue i exemplet nedan) innebär “värdet” en referens till objektet av typen MyValue, vilket innebär att även om m (i MyFunction) är en kopia av myValue, så refererar både m och myValue till samma objekt på heapen. Så när den instansen ändras genom m i funktionen MyFunction, så slår det igenom på myValue.
var myValue = new MyValue {X = 10};
MyFuction(myValue);
Console.WriteLine(myValue.X);
void MyFuction(MyValue m)
{
m.X = 20;
}
public class MyValue
{
public int X { get; set; }
}
Resultatet av körningen blir alltså 20.
Vill man se skillnaden mellan att skicka referenstyper “by value” och “by reference” så kan man tilldela ett nytt värde till själva variabeln (m). Eftersom värdet i m är en kopia av värdet i myValue, men myValue är en referensvariabel, kan vi sätta m till en ny referens utan att påverka ursprungsvariabeln myValue.
var myValue = new MyValue {X = 10};
MyFuction(myValue);
Console.WriteLine(myValue.X);
void MyFuction(MyValue m)
{
m = new MyValue {X = 20};
}
public class MyValue
{
public int X { get; set; }
}
Resultatet av körningen är 10, eftersom ursprungsvärdet inte förändrades. Variabeln m fick ett nytt objekt att referera till.
Skulle däremot parametern skickas in “by reference”, skriver new över ursprungsvärdet i myValue, så att den pekar på det nya objektet.
var myValue = new MyValue {X = 10};
MyFuction(ref myValue);
Console.WriteLine(myValue.X);
void MyFuction(ref MyValue m)
{
m = new MyValue {X = 20};
}
public class MyValue
{
public int X { get; set; }
}
Resultatet av körningen blir därför 20.
Funktioner som ger en retur, ger den “by value”, och det kan vara värt att tänka på om man hanterar större värdetyper. Koden nedan utför just ingenting, men trots att myValue skickas in “by reference” ska skapas en kopia vid return. Eftersom det som kopieras är en referensvariabel så är inte priset särskilt högt, men variabeln kopieras likväl.
var myValue = new MyValue { X = 10 };
myValue = MyFuction(ref myValue);
Console.WriteLine(myValue.X);
MyValue MyFuction(ref MyValue m)
{
return m;
}
public class MyValue
{
public int X { get; set; }
}
Precis som argument kan tas emot “by value” eller “by reference” kan även returer ske “by value” eller “by reference”, och precis som med argument så är det “by value” som gäller om inget annat sägs. Betrakta följande kod:
var x = 10;
var y = DoSomething(ref x);
x++;
Console.WriteLine(y);
int DoSomething(ref int x)
{
x++;
return x;
}
Rad 4 ger 11. Eftersom värdet kopieras på return-satsen, är det kopian som ökas till 12 på rad 3. Genom att skicka tillbaka värdet “by reference” får man 12:
var x = 10;
ref int y = ref DoSomething(ref x);
x++;
Console.WriteLine(y);
ref int DoSomething(ref int x)
{
x++;
return ref x;
}
Att skicka tillbaka värden “by reference” kan dels undvika att stora strukturer kopieras där det inte är önskvärt, och skulle t.ex. kunna användas för att låta annan kod manipulera ett element i en array. I detta exempel ger funktionen GiveElement ut ett element extern kod att manipulera.
var arr = new[]{1,2,3,4,5};
foreach (var i in arr)
Console.WriteLine(i);
ref int x = ref GiveElement(ref arr);
x = 10;
foreach (var i in arr)
Console.WriteLine(i);
ref int GiveElement(ref int[] arr)
{
return ref arr[3];
}
Andra gången arrayen skrivs ut, är värdet av det fjärde elementet (index 3) ändrat till 10.
Del 1/2: Referenstyper eller värdetyper
Categories: C#
Bjud mig på en kopp kaffe (20:-) som tack för bra innehåll!
Leave a Reply