Delen via


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.
  • 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 voor in parameters.

Leden van een klas kunnen geen handtekeningen hebben die alleen verschillen per ref, ref readonlyof inout. 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 readonlyof in parameter heeftout. Methoden kunnen echter worden overbelast wanneer een methode een ref, ref readonlyinof 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 readonlyen 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 de ref wijzigingsfunctie bevatten.
  • Een argument voor een out parameter moet de out wijzigingsfunctie bevatten.
  • Een argument voor een in parameter kan eventueel de in wijzigingsfunctie bevatten. Als de ref wijzigingsfunctie wordt gebruikt voor het argument, geeft de compiler een waarschuwing.
  • Een argument voor een ref readonly parameter moet de in of ref 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 geen struct, of een algemeen type niet beperkt is om een struct te zijn.
  • De ref readonly trefwoorden en in trefwoorden kunnen alleen worden gebruikt als het eerste argument een struct.
  • De ref readonly trefwoorden en in 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 overeenkomende in parameter. Als er anders slechts twee methoden aanwezig zijn in, is de by-waarde-overbelasting een betere overeenkomst.
  • Door op inte geven, declareert u uw intentie om een argument door te geven op basis van verwijzing. Het argument waarmee in wordt gebruikt, moet een locatie vertegenwoordigen waarnaar rechtstreeks kan worden verwezen. Dezelfde algemene regels voor out en ref argumenten zijn van toepassing: u kunt geen constanten, gewone eigenschappen of andere expressies gebruiken die waarden produceren. Als u anders weglaat in 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 met in 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.

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:

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.