Methodeparameters
Standaard worden argumenten in C# doorgegeven aan functies op waarde. Dit betekent dat een kopie van de variabele wordt doorgegeven aan de methode. Voor waardetypen wordtstruct
een kopie van de waarde doorgegeven aan de methode. Voor verwijzingstypen wordtclass
een kopie van de verwijzing doorgegeven aan de methode. Met parameteraanpassingen kunt u argumenten doorgeven op basis van verwijzing. De volgende concepten helpen u deze verschillen te begrijpen en de parameteraanpassingen te gebruiken:
- Pass by value betekent dat een kopie van de variabele wordt doorgegeven aan de methode.
- Pass by reference betekent het doorgeven van toegang tot de variabele aan de methode.
- Een variabele van een verwijzingstype bevat een verwijzing naar de gegevens.
- Een variabele van een waardetype bevat de gegevens rechtstreeks.
Omdat een struct een waardetype is, ontvangt en werkt de methode op een kopie van het structargument wanneer u een struct per waarde doorgeeft aan een methode. De methode heeft geen toegang tot de oorspronkelijke struct in de aanroepmethode en kan deze daarom op geen enkele manier wijzigen. De methode kan alleen de kopie wijzigen.
Een klasse-exemplaar is een verwijzingstype dat geen waardetype is. Wanneer een verwijzingstype wordt doorgegeven aan een methode, ontvangt de methode een kopie van de verwijzing naar het klasse-exemplaar. Beide variabelen verwijzen naar hetzelfde object. De parameter is een kopie van de verwijzing. De aangeroepen methode kan het exemplaar niet opnieuw toewijzen in de aanroepmethode. De aangeroepen methode kan echter de kopie van de verwijzing gebruiken om toegang te krijgen tot de leden van het exemplaar. Als de aangeroepen methode een exemplaarlid wijzigt, ziet de aanroepmethode deze wijzigingen ook omdat deze verwijst naar hetzelfde exemplaar.
De uitvoer van het volgende voorbeeld illustreert het verschil. De methode ClassTaker
wijzigt de waarde van het willIChange
veld omdat de methode het adres in de parameter gebruikt om het opgegeven veld van het klasse-exemplaar te vinden. Het willIChange
veld van de aanroepmethode in de aanroepmethode verandert niet van aanroepen StructTaker
omdat de waarde van het argument een kopie is van de struct zelf, niet een kopie van het adres. StructTaker
wijzigt de kopie en de kopie gaat verloren wanneer de aanroep StructTaker
is voltooid.
class TheClass
{
public string? willIChange;
}
struct TheStruct
{
public string willIChange;
}
class TestClassAndStruct
{
static void ClassTaker(TheClass c)
{
c.willIChange = "Changed";
}
static void StructTaker(TheStruct s)
{
s.willIChange = "Changed";
}
public static void Main()
{
TheClass testClass = new TheClass();
TheStruct testStruct = new TheStruct();
testClass.willIChange = "Not Changed";
testStruct.willIChange = "Not Changed";
ClassTaker(testClass);
StructTaker(testStruct);
Console.WriteLine("Class field = {0}", testClass.willIChange);
Console.WriteLine("Struct field = {0}", testStruct.willIChange);
}
}
/* Output:
Class field = Changed
Struct field = Not Changed
*/
Combinaties van parametertype en argumentmodus
Hoe een argument wordt doorgegeven en of het een verwijzingstype of waardetype is, bepaalt welke wijzigingen in het argument zichtbaar zijn vanuit de aanroeper:
- Wanneer u een waardetype op waarde doorgeeft:
- Als de methode de parameter toewijst om naar een ander object te verwijzen, zijn deze wijzigingen niet zichtbaar in de aanroeper.
- Als de methode de status wijzigt van het object waarnaar wordt verwezen door de parameter, zijn deze wijzigingen niet zichtbaar vanuit de aanroeper.
- Wanneer u een verwijzingstype op waarde doorgeeft:
- Als de methode de parameter toewijst om naar een ander object te verwijzen, zijn deze wijzigingen niet zichtbaar in de aanroeper.
- Als de methode de status wijzigt van het object waarnaar wordt verwezen door de parameter, zijn deze wijzigingen zichtbaar vanuit de aanroeper.
- Wanneer u een waardetype doorgeeft per verwijzing:
- Als de methode de parameter toewijst om te verwijzen naar een ander object met behulp van
ref =
, zijn deze wijzigingen niet zichtbaar vanuit de aanroeper. - Als de methode de status wijzigt van het object waarnaar wordt verwezen door de parameter, zijn deze wijzigingen zichtbaar vanuit de aanroeper.
- Als de methode de parameter toewijst om te verwijzen naar een ander object met behulp van
- Wanneer u een verwijzingstype doorgeeft per verwijzing:
- Als de methode de parameter toewijst om naar een ander object te verwijzen, zijn deze wijzigingen zichtbaar vanuit de aanroeper.
- Als de methode de status wijzigt van het object waarnaar wordt verwezen door de parameter, zijn deze wijzigingen zichtbaar vanuit de aanroeper.
Door een verwijzingstype door te geven, kan de aangeroepen methode het object vervangen waarnaar de verwijzingsparameter verwijst in de aanroeper. De opslaglocatie van het object wordt doorgegeven aan de methode als de waarde van de referentieparameter. Als u de waarde in de opslaglocatie van de parameter wijzigt (zodat deze verwijst naar een nieuw object), wijzigt u ook de opslaglocatie waarnaar de aanroeper verwijst. In het volgende voorbeeld wordt een exemplaar van een verwijzingstype doorgegeven als een ref
parameter.
class Product
{
public Product(string name, int newID)
{
ItemName = name;
ItemID = newID;
}
public string ItemName { get; set; }
public int ItemID { get; set; }
}
private static void ChangeByReference(ref Product itemRef)
{
// Change the address that is stored in the itemRef parameter.
itemRef = new Product("Stapler", 12345);
}
private static void ModifyProductsByReference()
{
// Declare an instance of Product and display its initial values.
Product item = new Product("Fasteners", 54321);
System.Console.WriteLine("Original values in Main. Name: {0}, ID: {1}\n",
item.ItemName, item.ItemID);
// Pass the product instance to ChangeByReference.
ChangeByReference(ref item);
System.Console.WriteLine("Calling method. Name: {0}, ID: {1}\n",
item.ItemName, item.ItemID);
}
// This method displays the following output:
// Original values in Main. Name: Fasteners, ID: 54321
// Calling method. Name: Stapler, ID: 12345
Veilige context van verwijzingen en waarden
Methoden kunnen de waarden van parameters opslaan in velden. Wanneer parameters worden doorgegeven door een waarde, is dat meestal veilig. Waarden worden gekopieerd en referentietypen zijn bereikbaar wanneer ze zijn opgeslagen in een veld. Als u parameters op basis van verwijzing op een veilige manier doorgeeft, moet de compiler definiëren wanneer het veilig is om een verwijzing naar een nieuwe variabele toe te wijzen. Voor elke expressie definieert de compiler een veilige context die de toegang tot een expressie of variabele begrenst. De compiler maakt gebruik van twee bereiken: veilige context en ref-safe-context.
- De veilige context definieert het bereik waar elke expressie veilig kan worden geopend.
- De ref-safe-context definieert het bereik waar een verwijzing naar een expressie veilig kan worden geopend of gewijzigd.
Informeel kunt u deze bereiken beschouwen als het mechanisme om ervoor te zorgen dat uw code nooit toegang krijgt tot of een verwijzing wijzigt die niet langer geldig is. Een verwijzing is geldig zolang deze verwijst naar een geldig object of een geldige struct. De veilige context bepaalt wanneer een variabele kan worden toegewezen of opnieuw kan worden toegewezen. De ref-safe-context definieert wanneer een variabele kan worden toegewezen of opnieuw kan worden toegewezen. Toewijzing wijst een variabele toe aan een nieuwe waarde; Verw-toewijzing wijst de variabele toe om te verwijzen naar een andere opslaglocatie.
Referentieparameters
U past een van de volgende modifiers toe op een parameterdeclaratie om argumenten door te geven per verwijzing in plaats van op waarde:
ref
: Het argument moet worden geïnitialiseerd voordat u de methode aanroept. De methode kan een nieuwe waarde toewijzen aan de parameter, maar is hiervoor niet vereist.out
: De aanroepmethode is niet vereist om het argument te initialiseren voordat u de methode aanroept. De methode moet een waarde toewijzen aan de parameter.ref readonly
: Het argument moet worden geïnitialiseerd voordat u de methode aanroept. De methode kan geen nieuwe waarde toewijzen aan de parameter.in
: Het argument moet worden geïnitialiseerd voordat u de methode aanroept. De methode kan geen nieuwe waarde toewijzen aan de parameter. De compiler kan een tijdelijke variabele maken voor het opslaan van een kopie van het argument voorin
parameters.
Leden van een klas kunnen geen handtekeningen hebben die alleen verschillen per ref
, ref readonly
of in
out
. Er treedt een compilerfout op als het enige verschil tussen twee leden van een type is dat een van deze onderdelen een parameter heeft en de andere een ref
, ref readonly
of in
parameter heeftout
. Methoden kunnen echter worden overbelast wanneer een methode een ref
, ref readonly
in
of parameter out
heeft en de andere een parameter heeft die wordt doorgegeven door een waarde, zoals wordt weergegeven in het volgende voorbeeld. In andere situaties waarin handtekeningkoppeling is vereist, zoals verbergen of overschrijven, in
, ref
, , ref readonly
en out
die deel uitmaken van de handtekening en niet met elkaar overeenkomen.
Wanneer een parameter een van de voorgaande modifiers heeft, kan het bijbehorende argument een compatibele wijzigingsfunctie hebben:
- Een argument voor een
ref
parameter moet deref
wijzigingsfunctie bevatten. - Een argument voor een
out
parameter moet deout
wijzigingsfunctie bevatten. - Een argument voor een
in
parameter kan eventueel dein
wijzigingsfunctie bevatten. Als deref
wijzigingsfunctie wordt gebruikt voor het argument, geeft de compiler een waarschuwing. - Een argument voor een
ref readonly
parameter moet dein
ofref
modifiers bevatten, maar niet beide. Als geen van beide modifier is opgenomen, geeft de compiler een waarschuwing.
Wanneer u deze modifiers gebruikt, beschrijven ze hoe het argument wordt gebruikt:
ref
betekent dat de methode de waarde van het argument kan lezen of schrijven.out
betekent dat de methode de waarde van het argument instelt.ref readonly
betekent dat de methode wordt gelezen, maar de waarde van het argument niet kan worden geschreven. Het argument moet worden doorgegeven met verwijzing.in
betekent dat de methode wordt gelezen, maar de waarde van het argument niet kan worden geschreven. Het argument wordt doorgegeven via een verwijzing of via een tijdelijke variabele.
U kunt de vorige parameteraanpassingen niet gebruiken in de volgende soorten methoden:
- Asynchrone methoden, die u definieert met behulp van de asynchrone wijziging.
- Iterator-methoden, waaronder een rendements- of
yield break
instructie.
Extensiemethoden hebben ook beperkingen voor het gebruik van deze argumenttrefwoorden:
- Het
out
trefwoord kan niet worden gebruikt voor het eerste argument van een extensiemethode. - Het
ref
trefwoord kan niet worden gebruikt voor het eerste argument van een extensiemethode als het argument geenstruct
, of een algemeen type niet beperkt is om een struct te zijn. - De
ref readonly
trefwoorden enin
trefwoorden kunnen alleen worden gebruikt als het eerste argument eenstruct
. - De
ref readonly
trefwoorden enin
trefwoorden kunnen niet worden gebruikt voor een algemeen type, zelfs niet als ze beperkt zijn tot een struct.
Eigenschappen zijn geen variabelen. Het zijn methoden. Eigenschappen kunnen geen argumenten zijn voor ref
parameters.
ref
parameteraanpassing
Als u een ref
parameter wilt gebruiken, moeten zowel de methodedefinitie als de aanroepende methode expliciet het ref
trefwoord gebruiken, zoals wordt weergegeven in het volgende voorbeeld. (Behalve dat de aanroepmethode kan weglaten ref
bij het maken van een COM-aanroep.)
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45
Een argument dat wordt doorgegeven aan een ref
parameter, moet worden geïnitialiseerd voordat deze wordt doorgegeven.
out
parameteraanpassing
Als u een out
parameter wilt gebruiken, moeten zowel de methodedefinitie als de aanroepmethode expliciet het out
trefwoord gebruiken. Voorbeeld:
int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod); // value is now 44
void OutArgExample(out int number)
{
number = 44;
}
Variabelen die als out
argumenten worden doorgegeven, hoeven niet te worden geïnitialiseerd voordat ze worden doorgegeven in een methode-aanroep. De aangeroepen methode is echter vereist om een waarde toe te wijzen voordat de methode wordt geretourneerd.
Deconstruct-methoden declareren hun parameters met de out
modifier om meerdere waarden te retourneren. Andere methoden kunnen waarde-tuples retourneren voor meerdere retourwaarden.
U kunt een variabele declareren in een afzonderlijke instructie voordat u deze als argument out
doorgeeft. U kunt de out
variabele ook declareren in de argumentenlijst van de methodeaanroep in plaats van in een afzonderlijke variabeledeclaratie. out
variabeledeclaraties produceren compactere, leesbare code en voorkomen ook dat u per ongeluk een waarde aan de variabele toewijst vóór de methodeaanroep. In het volgende voorbeeld wordt de number
variabele in de aanroep naar de methode Int32.TryParse gedefinieerd.
string numberAsString = "1640";
if (Int32.TryParse(numberAsString, out int number))
Console.WriteLine($"Converted '{numberAsString}' to {number}");
else
Console.WriteLine($"Unable to convert '{numberAsString}'");
// The example displays the following output:
// Converted '1640' to 1640
U kunt ook een impliciet getypte lokale variabele declareren.
ref readonly
Modifier
De ref readonly
modifier moet aanwezig zijn in de methodedeclaratie. Een wijzigingsfunctie op de oproepsite is optioneel. in
De of ref
modifier kan worden gebruikt. De ref readonly
wijzigingsfunctie is niet geldig op de oproepsite. Welke wijzigingsfunctie u op de oproepsite gebruikt, kan helpen bij het beschrijven van kenmerken van het argument. U kunt alleen gebruiken ref
als het argument een variabele is en beschrijfbaar is. U kunt het argument alleen gebruiken in
als het argument een variabele is. Het kan beschrijfbaar of alleen-lezen zijn. U kunt geen van beide wijzigingsaanpassingen toevoegen als het argument geen variabele is, maar een expressie is. In de volgende voorbeelden ziet u deze voorwaarden. De volgende methode maakt gebruik van de ref readonly
modifier om aan te geven dat een grote struct moet worden doorgegeven door verwijzing om prestatieredenen:
public static void ForceByRef(ref readonly OptionStruct thing)
{
// elided
}
U kunt de methode aanroepen met behulp van de ref
of in
modifier. Als u de wijzigingsfunctie weglaat, geeft de compiler een waarschuwing. Wanneer het argument een expressie is, geen variabele, kunt u de in
of ref
modifiers niet toevoegen, dus moet u de waarschuwing onderdrukken:
ForceByRef(in options);
ForceByRef(ref options);
ForceByRef(options); // Warning! variable should be passed with `ref` or `in`
ForceByRef(new OptionStruct()); // Warning, but an expression, so no variable to reference
Als de variabele een readonly
variabele is, moet u de in
wijzigingsfunctie gebruiken. De compiler geeft een fout op als u in plaats daarvan de ref
wijzigingsfunctie gebruikt.
De ref readonly
wijzigingsfunctie geeft aan dat de methode verwacht dat het argument een variabele is in plaats van een expressie die geen variabele is. Voorbeelden van expressies die geen variabelen zijn, zijn constanten, retourwaarden van methoden en eigenschappen. Als het argument geen variabele is, geeft de compiler een waarschuwing.
in
parameteraanpassing
De in
wijzigingsfunctie is vereist in de methodedeclaratie, maar is niet nodig op de oproepsite.
int readonlyArgument = 44;
InArgExample(readonlyArgument);
Console.WriteLine(readonlyArgument); // value is still 44
void InArgExample(in int number)
{
// Uncomment the following line to see error CS8331
//number = 19;
}
Met de in
wijzigingsfunctie kan de compiler een tijdelijke variabele voor het argument maken en een alleen-lezen verwijzing naar dat argument doorgeven. De compiler maakt altijd een tijdelijke variabele wanneer het argument moet worden geconverteerd, wanneer er een impliciete conversie van het argumenttype is of wanneer het argument een waarde is die geen variabele is. Als het argument bijvoorbeeld een letterlijke waarde is of de waarde die wordt geretourneerd door een eigenschapstoegangsfunctie. Wanneer uw API vereist dat het argument wordt doorgegeven via verwijzing, kiest u de ref readonly
wijzigingsfunctie in plaats van de in
wijzigingsfunctie.
Methoden die zijn gedefinieerd met behulp van in
parameters kunnen prestatieoptimalisatie opleveren. Sommige struct
typeargumenten kunnen groot zijn en wanneer methoden worden aangeroepen in strakke lussen of kritieke codepaden, zijn de kosten voor het kopiëren van deze structuren aanzienlijk. Methoden declareren in
parameters om op te geven dat argumenten veilig kunnen worden doorgegeven door verwijzing, omdat de aangeroepen methode de status van dat argument niet wijzigt. Het doorgeven van deze argumenten per verwijzing voorkomt de (mogelijk) dure kopie. U voegt de in
wijzigingsfunctie expliciet toe op de aanroepsite om ervoor te zorgen dat het argument wordt doorgegeven via verwijzing, niet op waarde. Expliciet gebruiken heeft in
de volgende twee effecten:
in
Als u op de aanroepsite opgeeft, moet de compiler een methode selecteren die is gedefinieerd met een overeenkomendein
parameter. Als er anders slechts twee methoden aanwezig zijnin
, is de by-waarde-overbelasting een betere overeenkomst.- Door op
in
te geven, declareert u uw intentie om een argument door te geven op basis van verwijzing. Het argument waarmeein
wordt gebruikt, moet een locatie vertegenwoordigen waarnaar rechtstreeks kan worden verwezen. Dezelfde algemene regels voorout
enref
argumenten zijn van toepassing: u kunt geen constanten, gewone eigenschappen of andere expressies gebruiken die waarden produceren. Als u anders weglaatin
op de aanroepsite, wordt de compiler geïnformeerd dat het prima is om een tijdelijke variabele te maken om alleen-lezenverwijzing naar de methode door te geven. De compiler maakt een tijdelijke variabele om verschillende beperkingen metin
argumenten te overwinnen:- Met een tijdelijke variabele kunt u tijdconstanten compileren als
in
parameters. - Een tijdelijke variabele staat eigenschappen of andere expressies voor
in
parameters toe. - Een tijdelijke variabele staat argumenten toe waarbij er een impliciete conversie van het argumenttype naar het parametertype is.
- Met een tijdelijke variabele kunt u tijdconstanten compileren als
In alle voorgaande exemplaren maakt de compiler een tijdelijke variabele waarin de waarde van de constante, eigenschap of andere expressie wordt opgeslagen.
De volgende code illustreert deze regels:
static void Method(in int argument)
{
// implementation removed
}
Method(5); // OK, temporary variable created.
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // OK, temporary int created with the value 0
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // passed by readonly reference
Method(in i); // passed by readonly reference, explicitly using `in`
Stel nu dat er een andere methode beschikbaar was met behulp van argumenten voor waarden. De resultaten worden gewijzigd, zoals wordt weergegeven in de volgende code:
static void Method(int argument)
{
// implementation removed
}
static void Method(in int argument)
{
// implementation removed
}
Method(5); // Calls overload passed by value
Method(5L); // CS1503: no implicit conversion from long to int
short s = 0;
Method(s); // Calls overload passed by value.
Method(in s); // CS1503: cannot convert from in short to in int
int i = 42;
Method(i); // Calls overload passed by value
Method(in i); // passed by readonly reference, explicitly using `in`
De enige methode-aanroep waarbij het argument wordt doorgegeven door verwijzing, is de laatste.
Notitie
De voorgaande code gebruikt int
als argumenttype voor eenvoud. Omdat int
dit niet groter is dan een verwijzing in de meeste moderne machines, is het niet handig om één int
verwijzing als een alleen-lezen verwijzing door te geven.
params
Modifier
Er zijn geen andere parameters toegestaan na het params
trefwoord in een methodedeclaratie en slechts één params
trefwoord is toegestaan in een methodedeclaratie.
Het gedeclareerde type van de params
parameter moet een verzamelingstype zijn. Herkende verzamelingstypen zijn:
- Een enkeldimensionaal matrixtype
T[]
, in dat geval het elementtype .T
- Een type span:
System.Span<T>
System.ReadOnlySpan<T>
Hier isT
het elementtype.
- Een type met een toegankelijke methode voor maken met een bijbehorend elementtype. De methode create wordt geïdentificeerd met hetzelfde kenmerk dat wordt gebruikt voor verzamelingsexpressies.
- Een struct of klassetype dat implementeert System.Collections.Generic.IEnumerable<T> waar:
- Het type heeft een constructor die zonder argumenten kan worden aangeroepen en de constructor ten minste net zo toegankelijk is als het declaratielid.
- Het type heeft een instantiemethode
Add
(geen extensiemethode), waarbij:- De methode kan worden aangeroepen met één waardeargument.
- Als de methode algemeen is, kunnen de typeargumenten worden afgeleid van het argument.
- De methode is minstens zo toegankelijk als het declarerend lid. Hier is het elementtype het iteratietype van het type.
- Een interfacetype:
Vóór C# 13 moet de parameter één dimensionale matrix zijn.
Wanneer u een methode aanroept met een params
parameter, kunt u het volgende doorgeven:
- Een door komma's gescheiden lijst met argumenten van het type matrixelementen.
- Een verzameling argumenten van het opgegeven type.
- Geen argumenten. Als u geen argumenten verzendt, is de lengte van de
params
lijst nul.
In het volgende voorbeeld ziet u verschillende manieren waarop argumenten naar een params
parameter kunnen worden verzonden.
public static void ParamsModifierExample(params int[] list)
{
for (int i = 0; i < list.Length; i++)
{
System.Console.Write(list[i] + " ");
}
System.Console.WriteLine();
}
public static void ParamsModifierObjectExample(params object[] list)
{
for (int i = 0; i < list.Length; i++)
{
System.Console.Write(list[i] + " ");
}
System.Console.WriteLine();
}
public static void TryParamsCalls()
{
// You can send a comma-separated list of arguments of the
// specified type.
ParamsModifierExample(1, 2, 3, 4);
ParamsModifierObjectExample(1, 'a', "test");
// A params parameter accepts zero or more arguments.
// The following calling statement displays only a blank line.
ParamsModifierObjectExample();
// An array argument can be passed, as long as the array
// type matches the parameter type of the method being called.
int[] myIntArray = { 5, 6, 7, 8, 9 };
ParamsModifierExample(myIntArray);
object[] myObjArray = { 2, 'b', "test", "again" };
ParamsModifierObjectExample(myObjArray);
// The following call causes a compiler error because the object
// array cannot be converted into an integer array.
//ParamsModifierExample(myObjArray);
// The following call does not cause an error, but the entire
// integer array becomes the first element of the params array.
ParamsModifierObjectExample(myIntArray);
}
/*
Output:
1 2 3 4
1 a test
5 6 7 8 9
2 b test again
System.Int32[]
*/
Overbelastingsresolutie kan dubbelzinnigheid veroorzaken wanneer het argument voor een params
parameter een verzamelingstype is. Het verzamelingstype van het argument moet worden omgezet in het verzamelingstype van de parameter. Wanneer verschillende overbelastingen betere conversies bieden voor die parameter, kan die methode beter zijn. Als het argument voor de params
parameter echter discrete elementen bevat of ontbreekt, zijn alle overbelastingen met verschillende params
parametertypen gelijk aan die parameter.
Zie de sectie over argumentlijsten in de C#-taalspecificatie voor meer informatie. De taalspecificatie is de definitieve bron voor de C#-syntaxis en het gebruik.