Metodparametrar
Som standard skickas argument i C# till funktioner efter värde. Det innebär att en kopia av variabeln skickas till metoden. För värdetyper (struct
) skickas en kopia av värdet till metoden. För referenstyper (class
) skickas en kopia av referensen till metoden. Med parametermodifierare kan du skicka argument med referens. Följande begrepp hjälper dig att förstå dessa skillnader och hur du använder parametermodifierarna:
- Skicka efter värde innebär att en kopia av variabeln skickas till metoden.
- Skicka med referens innebär att du skickar åtkomst till variabeln till -metoden.
- En variabel av en referenstyp innehåller en referens till dess data.
- En variabel av en värdetyp innehåller dess data direkt.
Eftersom en struct är en värdetyp tar metoden emot och fungerar på en kopia av struct-argumentet när du skickar en struct efter värde till en metod. Metoden har ingen åtkomst till den ursprungliga structen i anropande metod och kan därför inte ändra den på något sätt. Metoden kan bara ändra kopian.
En klassinstans är en referenstyp som inte är en värdetyp. När en referenstyp skickas med ett värde till en metod tar metoden emot en kopia av referensen till klassinstansen. Båda variablerna refererar till samma objekt. Parametern är en kopia av referensen. Den anropade metoden kan inte omtilldela instansen i anropande metod. Den anropade metoden kan dock använda kopian av referensen för att komma åt instansmedlemmarna. Om den anropade metoden ändrar en instansmedlem ser anropande metoden också dessa ändringar eftersom den refererar till samma instans.
Utdata från följande exempel illustrerar skillnaden. Metoden ClassTaker
ändrar värdet willIChange
för fältet eftersom metoden använder adressen i parametern för att hitta det angivna fältet i klassinstansen. Fältet willIChange
i structen i anropande metod ändras inte från anrop StructTaker
eftersom argumentets värde är en kopia av själva structen, inte en kopia av dess adress. StructTaker
ändrar kopian och kopian går förlorad när anropet till StructTaker
har slutförts.
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
*/
Kombinationer av parametertyp och argumentläge
Hur ett argument skickas och om det är en referenstyp eller värdetyp styr vilka ändringar som görs i argumentet som visas från anroparen:
- När du skickar en värdetyp efter värde:
- Om metoden tilldelar parametern för att referera till ett annat objekt visas inte dessa ändringar från anroparen.
- Om metoden ändrar tillståndet för det objekt som anges av parametern visas inte dessa ändringar från anroparen.
- När du skickar en referenstyp efter värde:
- Om metoden tilldelar parametern för att referera till ett annat objekt visas inte dessa ändringar från anroparen.
- Om metoden ändrar tillståndet för det objekt som anges av parametern visas dessa ändringar från anroparen.
- När du skickar en värdetyp efter referens:
- Om metoden tilldelar parametern att referera till ett annat objekt med ,
ref =
visas inte dessa ändringar från anroparen. - Om metoden ändrar tillståndet för det objekt som anges av parametern visas dessa ändringar från anroparen.
- Om metoden tilldelar parametern att referera till ett annat objekt med ,
- När du skickar en referenstyp efter referens:
- Om metoden tilldelar parametern för att referera till ett annat objekt visas ändringarna från anroparen.
- Om metoden ändrar tillståndet för det objekt som anges av parametern visas dessa ändringar från anroparen.
Genom att skicka en referenstyp med referens kan den anropade metoden ersätta det objekt som referensparametern refererar till i anroparen. Lagringsplatsen för objektet skickas till metoden som värdet för referensparametern. Om du ändrar värdet på lagringsplatsen för parametern (för att peka på ett nytt objekt) ändrar du även lagringsplatsen som anroparen refererar till. I följande exempel skickas en instans av en referenstyp som en 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
Säker kontext för referenser och värden
Metoder kan lagra parametrarnas värden i fält. När parametrar skickas av värde är det vanligtvis säkert. Värden kopieras och referenstyper kan nås när de lagras i ett fält. Om parametrar skickas med referens på ett säkert sätt måste kompilatorn definiera när det är säkert att tilldela en referens till en ny variabel. För varje uttryck definierar kompilatorn en säker kontext som begränsar åtkomsten till ett uttryck eller en variabel. Kompilatorn använder två omfång: safe-context och ref-safe-context.
- Safe-context definierar omfånget där alla uttryck kan nås på ett säkert sätt.
- Ref-safe-context definierar omfånget där en referens till alla uttryck kan nås eller ändras på ett säkert sätt.
Informellt kan du se dessa omfång som en mekanism för att se till att koden aldrig kommer åt eller ändrar en referens som inte längre är giltig. En referens är giltig så länge den refererar till ett giltigt objekt eller en fjäder. Safe-context definierar när en variabel kan tilldelas eller omtilldelas. Ref-safe-context definierar när en variabel kan tilldelas eller omtilldelas. Tilldelning tilldelar en variabel till ett nytt värde. referenstilldelning tilldelar variabeln för att referera till en annan lagringsplats.
Referensparametrar
Du tillämpar någon av följande modifierare på en parameterdeklaration för att skicka argument efter referens i stället för efter värde:
ref
: Argumentet måste initieras innan metoden anropas. Metoden kan tilldela parametern ett nytt värde, men krävs inte för att göra det.out
: Anropande metod krävs inte för att initiera argumentet innan metoden anropas. Metoden måste tilldela parametern ett värde.ref readonly
: Argumentet måste initieras innan metoden anropas. Metoden kan inte tilldela parametern ett nytt värde.in
: Argumentet måste initieras innan metoden anropas. Metoden kan inte tilldela parametern ett nytt värde. Kompilatorn kan skapa en tillfällig variabel för att lagra en kopia av argumentet tillin
parametrar.
Medlemmar i en klass kan inte ha signaturer som bara skiljer sig åt med ref
, ref readonly
, in
eller out
. Ett kompilatorfel uppstår om den enda skillnaden mellan två medlemmar av en typ är att en av dem har en ref
parameter och den andra har en out
, ref readonly
eller in
parameter. Metoder kan dock överbelastas när en metod har parametern ref
, ref readonly
, in
eller out
och den andra har en parameter som skickas av värdet, som visas i följande exempel. I andra situationer som kräver signaturmatchning, som att dölja eller åsidosätta, , in
ref
, ref readonly
och out
är en del av signaturen och inte matchar varandra.
När en parameter har någon av föregående modifierare kan motsvarande argument ha en kompatibel modifierare:
- Ett argument för en
ref
parameter måste innehållaref
modifieraren. - Ett argument för en
out
parameter måste innehållaout
modifieraren. - Ett argument för en
in
parameter kan också innehållain
modifieraren.ref
Om modifieraren används på argumentet i stället utfärdar kompilatorn en varning. - Ett argument för en
ref readonly
parameter bör innehålla antingenin
ellerref
modifierare, men inte båda. Om ingen av modifierarna ingår utfärdar kompilatorn en varning.
När du använder dessa modifierare beskriver de hur argumentet används:
ref
innebär att metoden kan läsa eller skriva värdet för argumentet.out
innebär att metoden anger argumentets värde.ref readonly
innebär att metoden läser, men inte kan skriva argumentets värde. Argumentet ska skickas med referens.in
innebär att metoden läser, men inte kan skriva argumentets värde. Argumentet skickas med referens eller via en tillfällig variabel.
Du kan inte använda de tidigare parametermodifierarna i följande typer av metoder:
- Asynkrona metoder som du definierar med hjälp av asynkron modifieraren.
- Iteratormetoder, som innehåller en avkastningsretur eller
yield break
-instruktion.
Tilläggsmetoder har också begränsningar för användningen av dessa argumentnyckelord:
- Nyckelordet
out
kan inte användas på det första argumentet i en tilläggsmetod. - Nyckelordet
ref
kan inte användas på det första argumentet i en tilläggsmetod när argumentet inte är enstruct
, eller en allmän typ som inte är begränsad till att vara en struct. - Nyckelorden
ref readonly
ochin
kan inte användas om inte det första argumentet är ettstruct
. - Nyckelorden
ref readonly
ochin
kan inte användas på någon allmän typ, även om de är begränsade till att vara en struct.
Egenskaper är inte variabler. Det är metoder. Egenskaper kan inte vara argument för ref
parametrar.
ref
parametermodifierare
Om du vill använda en ref
parameter måste både metoddefinitionen och anropande metoden uttryckligen använda nyckelordet ref
, som du ser i följande exempel. (Förutom att anropsmetoden kan utelämna ref
när du gör ett COM-anrop.)
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45
Ett argument som skickas till en ref
parameter måste initieras innan det skickas.
out
parametermodifierare
Om du vill använda en out
parameter måste både metoddefinitionen och anropande metoden uttryckligen använda nyckelordet out
. Till exempel:
int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod); // value is now 44
void OutArgExample(out int number)
{
number = 44;
}
Variabler som skickas som out
argument behöver inte initieras innan de skickas i ett metodanrop. Den anropade metoden krävs dock för att tilldela ett värde innan metoden returneras.
Dekonstruera metoder deklarerar sina parametrar med out
modifieraren för att returnera flera värden. Andra metoder kan returnera värdetupplar för flera returvärden.
Du kan deklarera en variabel i en separat instruktion innan du skickar den som ett out
argument. Du kan också deklarera variabeln out
i argumentlistan för metodanropet i stället för i en separat variabeldeklaration. out
variabeldeklarationer ger mer kompakt, läsbar kod och förhindrar även att du oavsiktligt tilldelar ett värde till variabeln före metodanropet. I följande exempel definieras variabeln number
i anropet till metoden Int32.TryParse .
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
Du kan också deklarera en implicit inskriven lokal variabel.
ref readonly
modifierare
Modifieraren ref readonly
måste finnas i metoddeklarationen. En modifierare på anropsplatsen är valfri. in
Antingen kan modifieraren eller ref
användas. Modifieraren ref readonly
är inte giltig på anropsplatsen. Vilken modifierare du använder på anropsplatsen kan hjälpa dig att beskriva argumentets egenskaper. Du kan bara använda ref
om argumentet är en variabel och kan skrivas. Du kan bara använda in
när argumentet är en variabel. Det kan vara skrivbart eller skrivskyddat. Du kan inte lägga till någon av modifierarna om argumentet inte är en variabel, men är ett uttryck. Följande exempel visar dessa villkor. Följande metod använder ref readonly
modifieraren för att ange att en stor struct ska skickas med referens av prestandaskäl:
public static void ForceByRef(ref readonly OptionStruct thing)
{
// elided
}
Du kan anropa metoden med hjälp av ref
eller in
modifieraren. Om du utelämnar modifieraren utfärdar kompilatorn en varning. När argumentet är ett uttryck, inte en variabel, kan du inte lägga till in
eller ref
modifierare, så du bör ignorera varningen:
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
Om variabeln är en readonly
variabel måste du använda in
modifieraren. Kompilatorn utfärdar ett fel om du använder ref
modifieraren i stället.
Modifieraren ref readonly
anger att metoden förväntar sig att argumentet ska vara en variabel i stället för ett uttryck som inte är en variabel. Exempel på uttryck som inte är variabler är konstanter, metodreturvärden och egenskaper. Om argumentet inte är en variabel utfärdar kompilatorn en varning.
in
parametermodifierare
Modifieraren in
krävs i metoddeklarationen men onödig på anropsplatsen.
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;
}
Med in
modifieraren kan kompilatorn skapa en tillfällig variabel för argumentet och skicka en skrivskyddad referens till argumentet. Kompilatorn skapar alltid en tillfällig variabel när argumentet måste konverteras, när det finns en implicit konvertering från argumenttypen eller när argumentet är ett värde som inte är en variabel. Till exempel när argumentet är ett literalvärde eller värdet som returneras från en egenskapsåtkomst. När ditt API kräver att argumentet skickas med referens väljer du ref readonly
modifieraren i stället för in
modifieraren.
Metoder som definieras med parametrar in
kan få prestandaoptimering. Vissa struct
typargument kan vara stora och när metoder anropas i snäva loopar eller kritiska kodsökvägar är kostnaden för att kopiera dessa strukturer betydande. Metoder deklarerar in
parametrar för att ange att argument kan skickas med referens på ett säkert sätt eftersom den anropade metoden inte ändrar argumentets tillstånd. Om du skickar dessa argument med referens undviker du den (potentiellt) dyra kopian. Du lägger uttryckligen in
till modifieraren på anropsplatsen för att säkerställa att argumentet skickas med referens, inte efter värde. Explicit användning in
har följande två effekter:
- Om du
in
anger på anropsplatsen tvingar kompilatorn att välja en metod som definierats med en matchandein
parameter. Annars, när två metoder skiljer sig endast i närvaro avin
, är överlagringen efter värde en bättre matchning. - Genom att ange deklarerar
in
du avsikten att skicka ett argument med referens. Argumentet som används medin
måste representera en plats som kan refereras direkt till. Samma allmänna regler förout
ochref
argument gäller: Du kan inte använda konstanter, vanliga egenskaper eller andra uttryck som skapar värden. Annars meddelar utelämnandein
på anropsplatsen kompilatorn att det är bra att skapa en tillfällig variabel för att skicka med skrivskyddad referens till metoden. Kompilatorn skapar en tillfällig variabel för att övervinna flera begränsningar medin
argument:- En tillfällig variabel tillåter kompileringstidskonstanter som
in
parametrar. - En tillfällig variabel tillåter egenskaper eller andra uttryck för
in
parametrar. - En tillfällig variabel tillåter argument där det finns en implicit konvertering från argumenttypen till parametertypen.
- En tillfällig variabel tillåter kompileringstidskonstanter som
I alla föregående instanser skapar kompilatorn en tillfällig variabel som lagrar värdet för konstanten, egenskapen eller andra uttryck.
Följande kod illustrerar dessa regler:
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`
Anta nu att en annan metod med hjälp av by-value-argument var tillgänglig. Resultatet ändras enligt följande kod:
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`
Det enda metodanropet där argumentet skickas med referens är det sista.
Kommentar
Föregående kod används int
som argumenttyp för enkelhetens skull. Eftersom int
inte är större än en referens i de flesta moderna datorer finns det ingen fördel med att skicka en enda int
som en skrivskyddad referens.
params
modifierare
Inga andra parametrar tillåts efter nyckelordet params
i en metoddeklaration och endast ett params
nyckelord tillåts i en metoddeklaration.
Den deklarerade typen av params
parameter måste vara en samlingstyp. Identifierade samlingstyper är:
- En endimensionell matristyp
T[]
, i vilket fall elementtypen ärT
. - En intervalltyp:
System.Span<T>
System.ReadOnlySpan<T>
Här ärT
elementtypen .
- En typ med en tillgänglig skapa-metod med motsvarande elementtyp. Metoden create identifieras med samma attribut som används för samlingsuttryck.
- En struct - eller klasstyp som implementerar System.Collections.Generic.IEnumerable<T> var:
- Typen har en konstruktor som kan anropas utan argument och konstruktorn är minst lika tillgänglig som den deklarerande medlemmen.
- Typen har en instansmetod
Add
(inte ett tillägg) där:- Metoden kan anropas med ett argument med ett enda värde.
- Om metoden är generisk kan typargumenten härledas från argumentet.
- Metoden är minst lika tillgänglig som den deklarerande medlemmen. Här är elementtypen typ av iteration av typen.
- En gränssnittstyp:
Före C# 13 måste parametern vara en endimensionell matris.
När du anropar en metod med en params
parameter kan du skicka in:
- En kommaavgränsad lista med argument av typen av matriselement.
- En samling argument av den angivna typen.
- Inga argument. Om du inte skickar några argument är listans
params
längd noll.
I följande exempel visas olika sätt på vilka argument kan skickas till en params
parameter.
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[]
*/
- Argumentlistor i C#-språkspecifikationen. Språkspecifikationen är den slutgiltiga källan för C#-syntax och -användning.