Seit der zwölften Sprachversion von C# in .NET 8.0 gibt es für Methodenparameter auch den Modifizierer ref readonly
. Mit diesem Zusatz darf die Methode den empfangenen Wert beziehungsweise die empfangene Objektreferenz nicht ändern. Bei der Übergabe von Referenztypen per ref readonly
kann die Methode aber weiterhin die einzelnen Objektinhalte ändern.
Dr. Holger Schwichtenberg ist technischer Leiter des Expertennetzwerks www.IT-Visions.de, das mit 53 renommierten Experten zahlreiche mittlere und große Unternehmen durch Beratungen und Schulungen sowie bei der Softwareentwicklung unterstützt. Durch seine Auftritte auf zahlreichen nationalen und internationalen Fachkonferenzen sowie mehr als 90 Fachbücher und mehr als 1500 Fachartikel gehört Holger Schwichtenberg zu den bekanntesten Experten für .NET und Webtechniken in Deutschland.
Die folgende Tabelle stellt ref readonly
gegenüber mit anderen Parametermodifizierern. Wichtig für das Verhalten ist, ob als Parameter ein Wertetyp oder ein Referenztyp übergeben wird:
|
Parameter ist Wertetyp
|
Parameter ist Referenztyp
|
|
Methode kann Wert ändern
|
Methode kann einzelne Werte im übergebenen Objekt ändern
|
Methode kann neues Objekt zuweisen
|
Übergabe ohne Modifizierer
|
Ja, aber Aufrufer bekommt den neuen Wert nicht
|
Ja
|
Ja, aber Aufrufer bekommt das neue Objekt nicht
|
Übergabe mit in
|
Nein
|
Ja
|
Nein
|
Übergabe mit ref
|
Ja
|
Ja
|
Ja
|
Übergabe mit ref readonly (seit C# 12.0)
|
Nein
|
Ja
|
Nein
|
Übergabe mit out
|
Ja
|
Ja
|
Ja
|
Die folgenden drei Listings zeigen dazu Beispiele inklusive eines Screenshots der jeweiligen Bildschirmausgaben.
Der erste Code zeigt die Wirkung der Parametermodifizierer für Parameter als Wertetyp.
/// <summary>
/// Wertetypen an Methode übergeben
/// </summary>
public void ParameterValueTypes()
{
CUI.H2(nameof(ParameterValueTypes));
#region
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int e = 50;
CUI.H3("Der Aufrufer hat vorher folgende Werte:");
Console.WriteLine(a + ";" + b + ";" + c + ";" + d + ";" + e);
string r = ParameterDemoValueTypes(a, b, ref c, ref d, out e);
CUI.H3("Die Methode hat folgende Werte:");
Console.WriteLine(r); // 11;20;31;40
CUI.H3("Der Aufrufer hat nachher folgende Werte:");
Console.WriteLine(a + ";" + b + ";" + c + ";" + d + ";" + e);
#endregion
}
public string ParameterDemoValueTypes(int WertValue, in int WertIn,
ref int WertRef,
ref readonly int WertRefRO,
out int WertOut)
{
WertValue++;
// nicht erlaubt, da in-Wert: WertIn++;
WertRef++;
// WertRefRO++; // nicht erlaubt, da readonly
// nicht erlaubt, da noch nicht initialisiert: WertOut++;
WertOut = 41;
return WertValue.ToString() + ";" + WertIn.ToString() + ";"
+ WertRef.ToString() + ";" + WertOut.ToString();
}
Der Code erzeugt folgende Ausgabe:
(Bild: Holger Schwichtenberg)
Das folgende Listing nutzt einen Parameter als Referenztyp Parameter (class Counter
). Die Methode ändert den Wert im Objekt.
class Counter
{
public string Name { get; set; }
public int Value { get; set; }
public override string ToString() => Name + "=" + Value;
}
/// <summary>
/// Referenztypen an Methode übergeben, die Wert in dem Objekt ändert
/// </summary>
public void ParameterReferenceType1()
{
CUI.H2(nameof(ParameterReferenceType1));
Counter a = new Counter() { Name = "a", Value = 10 };
Counter b = new Counter() { Name = "b", Value = 20 };
Counter c = new Counter() { Name = "c", Value = 30 };
Counter d = new Counter() { Name = "d", Value = 40 };
Counter e = new Counter() { Name = "e", Value = 50 };
CUI.H3("Der Aufrufer hat vorher folgende Werte:");
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);
Console.WriteLine(e);
string r = ParameterDemoRef1(a, b, ref c, ref d, out e);
CUI.H3("Die Methode hat folgende Werte:");
Console.WriteLine(r);
CUI.H3("Der Aufrufer hat nachher folgende Werte:");
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);
Console.WriteLine(e);
}
public string ParameterDemoRef1(Counter WertValue, in Counter WertIn,
ref Counter WertRef,
ref readonly Counter WertRefRO,
out Counter WertOut)
{
WertValue.Value++;
WertIn.Value++;
WertRef.Value++;
WertRefRO.Value++;
WertOut = new Counter { Name = "d", Value = 41 };
return WertValue.ToString() + ";" + WertRef.ToString() + ";"
+ WertIn.ToString() + ";" + WertOut.ToString();
}
Der Code erzeugt folgende Ausgabe:
(Bild: Holger Schwichtenberg)
Im dritten Beispiel ist der Parameter ein Referenztyp (class Counter
). Die Methode ändert die Objektreferenz
class Counter
{
public string Name { get; set; }
public int Value { get; set; }
public override string ToString() => Name + "=" + Value;
}
/// <summary>
/// Referenztypen an Methode übergeben, die neues Objekt zuweist und ändert
/// </summary>
public void ParameterReferenceType2()
{
CUI.H2(nameof(ParameterReferenceType2));
Counter a = new Counter() { Name = "a", Value = 10 };
Counter b = new Counter() { Name = "b", Value = 20 };
Counter c = new Counter() { Name = "c", Value = 30 };
Counter d = new Counter() { Name = "d", Value = 40 };
Counter e = new Counter() { Name = "e", Value = 50 };
CUI.H3("Der Aufrufer hat vorher folgende Werte:");
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);
Console.WriteLine(e);
string r = ParameterDemoRef2(a, b, ref c, ref d, out e);
CUI.H3("Die Methode hat folgende Werte:");
Console.WriteLine(r);
CUI.H3("Der Aufrufer hat nachher folgende Werte:");
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);
Console.WriteLine(e);
}
public string ParameterDemoRef2(Counter WertValue, in Counter WertIn,
ref Counter WertRef,
ref readonly Counter WertRefRO,
out Counter WertOut)
{
WertValue = new Counter { Name = "a*", Value = 101 };
// nicht erlaubt:
// WertIn = new Counter { Name = "b*", Value = 100 };
WertRef = new Counter { Name = "c*", Value = 102 };
// nicht erlaubt, da readonly:
// WertRefRO = new Counter { Name = "c*", Value = 103 };
WertOut = new Counter { Name = "d*", Value = 104 };
return WertValue.ToString() + ";" + WertRef.ToString() + ";"
+ WertIn.ToString() + ";" + WertOut.ToString();
}
Der Code erzeugt folgende Ausgabe:
(Bild: Holger Schwichtenberg)
(rme)