Metódusparaméterek
Alapértelmezés szerint a C# argumentumokat a függvények érték szerint továbbítják. Ez azt jelenti, hogy a változó másolatát átadja a metódusnak. Értéktípusokstruct
esetén a metódus átadja az érték másolatát. Hivatkozási (class
) típusok esetén a hivatkozás egy példányát átadja a metódusnak. A paramétermódosítók segítségével hivatkozással adhat át argumentumokat. Az alábbi fogalmak segítenek megérteni ezeket a különbségeket és a paramétermódosítók használatát:
- Az érték szerinti átadás azt jelenti , hogy a változó egy példányát átadja a metódusnak.
- A továbbítás hivatkozással azt jelenti , hogy hozzáférést ad a változónak a metódusnak.
- Egy referenciatípus változója az adataira mutató hivatkozást tartalmaz.
- Egy értéktípusú változó közvetlenül tartalmazza az adatait.
Mivel a struktúra értéktípus, a metódus akkor fogadja és működteti az struct argumentum egy példányát, amikor érték alapján ad át egy szerkezetet egy metódusnak. A metódusnak nincs hozzáférése a hívási metódus eredeti szerkezetéhez, ezért semmilyen módon nem módosíthatja. A metódus csak a másolatot módosíthatja.
Az osztálypéldány nem értéktípus, hanem hivatkozástípus . Ha egy referenciatípust érték továbbít egy metódusnak, a metódus megkapja az osztálypéldányra mutató hivatkozás másolatát. Mindkét változó ugyanarra az objektumra hivatkozik. A paraméter a hivatkozás másolata. A hívott metódus nem tudja újból hozzárendelni a példányt a hívó metódusban. A hívott metódus azonban a hivatkozás másolatát használhatja a példánytagok eléréséhez. Ha a hívott metódus módosít egy példánytagot, a hívó metódus is látja ezeket a változásokat, mivel ugyanarra a példányra hivatkozik.
Az alábbi példa kimenete a különbséget szemlélteti. A metódus ClassTaker
módosítja a willIChange
mező értékét, mert a metódus a paraméter címével keresi meg az osztálypéldány megadott mezőjét. A willIChange
hívó metódusban a struktúra mezője nem változik a hívástól StructTaker
, mert az argumentum értéke maga a struktúra másolata, nem pedig a cím másolata. StructTaker
módosítja a másolatot, és a másolat elveszik a hívás StructTaker
befejezésekor.
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
*/
Paramétertípus és argumentum mód kombinációi
Az argumentumok átadásának menete, illetve az, hogy hivatkozástípusról vagy értéktípusról van-e szó, szabályozza, hogy az argumentumon végrehajtott módosítások milyen módosítások láthatók a hívótól:
- Ha érték szerint ad át egy értéktípust:
- Ha a metódus hozzárendeli a paramétert egy másik objektumra való hivatkozáshoz, ezek a módosítások nem láthatók a hívótól.
- Ha a metódus módosítja a paraméter által hivatkozott objektum állapotát, ezek a módosítások nem láthatók a hívótól.
- Ha érték szerint ad át egy referenciatípust:
- Ha a metódus hozzárendeli a paramétert egy másik objektumra való hivatkozáshoz, ezek a módosítások nem láthatók a hívótól.
- Ha a metódus módosítja a paraméter által hivatkozott objektum állapotát, ezek a módosítások láthatók a hívótól.
- Ha hivatkozás alapján ad át egy értéktípust:
- Ha a metódus egy másik objektumra
ref =
való hivatkozáshoz rendeli hozzá a paramétert, ezek a módosítások nem láthatók a hívótól. - Ha a metódus módosítja a paraméter által hivatkozott objektum állapotát, ezek a módosítások láthatók a hívótól.
- Ha a metódus egy másik objektumra
- Ha hivatkozási típust ad át:
- Ha a metódus hozzárendeli a paramétert egy másik objektumra való hivatkozáshoz, ezek a módosítások láthatók a hívótól.
- Ha a metódus módosítja a paraméter által hivatkozott objektum állapotát, ezek a módosítások láthatók a hívótól.
A referenciatípus hivatkozással történő átadásával a hívott metódus lecserélheti azt az objektumot, amelyre a referenciaparaméter hivatkozik a hívóban. Az objektum tárolási helye a referenciaparaméter értékeként lesz átadva a metódusnak. Ha módosítja a paraméter tárolási helyének értékét (hogy egy új objektumra mutasson), azt a tárolási helyet is módosítja, amelyre a hívó hivatkozik. Az alábbi példa egy referenciatípus egy példányát adja át paraméterként ref
.
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
Hivatkozások és értékek biztonságos környezete
A metódusok a paraméterek értékeit mezőkben tárolhatják. Ha a paramétereket érték alapján adja át, az általában biztonságos. Az értékek másolása és a referenciatípusok akkor érhetők el, ha egy mezőben vannak tárolva. A paraméterek biztonságos hivatkozással történő átadásához a fordítónak meg kell határoznia, hogy mikor lehet biztonságosan hozzárendelni egy hivatkozást egy új változóhoz. A fordító minden kifejezéshez meghatároz egy olyan biztonságos környezetet , amely egy kifejezéshez vagy változóhoz való hozzáférést köti. A fordító két hatókört használ: safe-context és ref-safe-context.
- A biztonságos környezet határozza meg azt a hatókört, amelyben bármely kifejezés biztonságosan elérhető.
- A ref-safe-context határozza meg azt a hatókört , amelyben bármely kifejezésre mutató hivatkozás biztonságosan elérhető vagy módosítható.
Informálisan úgy tekinthet ezekre a hatókörökre, mint a mechanizmusra, amely biztosítja, hogy a kód soha ne férhessen hozzá vagy módosítson egy már nem érvényes hivatkozást. A hivatkozás mindaddig érvényes, amíg érvényes objektumra vagy szerkezetre hivatkozik. A biztonságos környezet határozza meg, hogy mikor rendelhető hozzá vagy rendelhető hozzá újra egy változó. A ref-safe-context azt határozza meg, hogy mikor rendelhető hozzá újra egy változó, vagy mikor lehet újból hozzárendelni. A hozzárendelés egy változót rendel egy új értékhez; A ref-hozzárendelés hozzárendeli a változót egy másik tárolóhelyre való hivatkozáshoz.
Referenciaparaméterek
Az alábbi módosítók egyikét alkalmazza egy paraméterdeklarációra, hogy az argumentumokat hivatkozással adja át érték helyett:
ref
: A metódus meghívása előtt inicializálni kell az argumentumot. A metódus új értéket rendelhet a paraméterhez, de ehhez nincs szükség rá.out
: A metódus meghívása előtt nem szükséges inicializálni az argumentumot. A metódusnak hozzá kell rendelnie egy értéket a paraméterhez.ref readonly
: A metódus meghívása előtt inicializálni kell az argumentumot. A metódus nem tud új értéket hozzárendelni a paraméterhez.in
: A metódus meghívása előtt inicializálni kell az argumentumot. A metódus nem tud új értéket hozzárendelni a paraméterhez. A fordító létrehozhat egy ideiglenes változót, amely az argumentum egy másolatát tárolja a paramétereknekin
.
Az osztály tagjai nem rendelkezhetnek olyan aláírásokkal, amelyek csak az ref
, , ref readonly
in
vagy out
. Fordítóhiba akkor fordul elő, ha egy típus két tagja között az egyetlen különbség az, hogy az egyik paraméterrel rendelkezik ref
, a másik pedig egy out
, ref readonly
vagy in
paraméterrel. A metódusok azonban túlterhelhetők, ha az egyik metódus rendelkezik ref
egy , ref readonly
, in
vagy out
paraméterrel, a másik pedig egy érték által átadott paraméterrel, ahogyan az az alábbi példában látható. Más olyan helyzetekben, amelyekhez aláírás-egyeztetés szükséges, például elrejtés vagy felülbírálás, in
ref
ref readonly
az out
aláírás részét képezik, és nem egyeznek egymással.
Ha egy paraméter rendelkezik az előző módosítók egyikével, a megfelelő argumentum kompatibilis módosítóval rendelkezhet:
- Egy paraméter argumentumának
ref
tartalmaznia kell a módosító értékétref
. - Egy paraméter argumentumának
out
tartalmaznia kell aout
módosító értékét. - Egy paraméter argumentuma
in
opcionálisan tartalmazhatja ain
módosító értékét. Ha aref
módosító helyett az argumentumot használja, a fordító figyelmeztetést ad ki. - Egy paraméter argumentumának
ref readonly
tartalmaznia kell a módosítókat vagyref
ain
módosítókat, de mindkettőt nem. Ha egyik módosító sem szerepel a fájlban, a fordító figyelmeztetést ad ki.
Ha ezeket a módosítókat használja, az argumentum használatának módját írják le:
ref
azt jelenti, hogy a metódus képes az argumentum értékének olvasására vagy írására.out
azt jelenti, hogy a metódus beállítja az argumentum értékét.ref readonly
azt jelenti, hogy a metódus beolvassa, de nem tudja megírni az argumentum értékét. Az argumentumot hivatkozással kell átadni.in
azt jelenti, hogy a metódus beolvassa, de nem tudja megírni az argumentum értékét. Az argumentumot hivatkozással vagy ideiglenes változón keresztül adjuk át.
Az előző paramétermódosítókat nem használhatja a következő metódustípusokban:
- Aszinkron metódusok, amelyeket az aszinkron módosító használatával határoz meg.
- Iterátormetódusok, amelyek hozamhozamot vagy
yield break
kimutatást tartalmaznak.
A bővítménymetelyek az alábbi argumentumszavak használatára is korlátozva vannak:
- A
out
kulcsszó nem használható a bővítménymetódus első argumentumán. - A
ref
kulcsszó nem használható a bővítménymetódus első argumentumán, ha az argumentum nemstruct
egy , vagy egy általános típus, amely nem konstruktálásra van korlátozva. - A
ref readonly
kulcsszavak ésin
a kulcsszavak csak akkor használhatók, ha az első argumentum egystruct
. - A
ref readonly
kulcsszavak ésin
a kulcsszavak nem használhatók általános típuson, még akkor sem, ha a szerkezetre korlátozva van.
A tulajdonságok nem változók. Ezek metódusok. A tulajdonságok nem lehetnek paraméterek argumentumai ref
.
ref
paramétermódosító
Paraméter ref
használatához a metódusdefiníciónak és a hívási módszernek is explicit módon kell használnia a ref
kulcsszót, ahogyan az az alábbi példában is látható. (Kivéve, hogy a hívási módszer kihagyhatja ref
a COM-hívásokat.)
void Method(ref int refArgument)
{
refArgument = refArgument + 44;
}
int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45
A paraméternek ref
átadott argumentumot inicializálni kell, mielőtt az át lett volna adva.
out
paramétermódosító
Paraméter használatához out
a metódusdefiníciónak és a hívási módszernek is explicit módon kell használnia a kulcsszót out
. Példa:
int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod); // value is now 44
void OutArgExample(out int number)
{
number = 44;
}
Az argumentumként out
átadott változókat nem kell inicializálni a metódushívásban való átadás előtt. A metódus visszatérése előtt azonban az úgynevezett metódusnak kell hozzárendelnie egy értéket.
A dekonstruálási metódusok a módosítóval deklarálják a out
paramétereket, hogy több értéket adjanak vissza. Más metódusok több visszatérési értékhez is visszaadhatják az értékeket .
Egy változót külön utasításban deklarálhat, mielőtt argumentumként out
ad át. A változót a out
metódushívás argumentumlistájában is deklarálhatja, nem pedig külön változódeklarációban. out
a változódeklarációk tömörebb, olvashatóbb kódot eredményeznek, és megakadályozzák, hogy véletlenül értéket rendeljen a változóhoz a metódushívás előtt. Az alábbi példa az number
Int32.TryParse metódus hívásában definiálja a változót.
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
Implicit módon beírt helyi változót is deklarálhat.
ref readonly
módosító
A ref readonly
módosítónak szerepelnie kell a metódus deklarációjában. A hívási helyen nem kötelező módosító. A módosító vagy ref
a in
módosító is használható. A ref readonly
módosító érvénytelen a hívási helyen. A hívási helyen használt módosító segíthet az argumentum jellemzőinek leírásában. Csak akkor használható ref
, ha az argumentum változó, és írható. Csak akkor használható in
, ha az argumentum változó. Írható vagy olvasható lehet. Egyik módosító sem vehető fel, ha az argumentum nem változó, hanem kifejezés. Az alábbi példák ezeket a feltételeket mutatják be. A következő módszer a ref readonly
módosító használatával jelzi, hogy egy nagy szerkezetet teljesítménybeli okokból kell átadni:
public static void ForceByRef(ref readonly OptionStruct thing)
{
// elided
}
A metódust meghívhatja a módosító vagy in
a ref
módosító használatával. Ha kihagyja a módosítót, a fordító figyelmeztetést ad ki. Ha az argumentum kifejezés, nem változó, akkor nem lehet hozzáadni a módosítókat vagy ref
a in
módosítókat, ezért el kell kerülnie a figyelmeztetést:
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
Ha a változó változó readonly
, akkor a in
módosítót kell használnia. A fordító hibát ad ki, ha ehelyett a ref
módosítót használja.
A ref readonly
módosító azt jelzi, hogy a metódus az argumentumot nem változóként, hanem változóként várja meg. A nem változó kifejezések például állandók, metódusok visszatérési értékei és tulajdonságai. Ha az argumentum nem változó, a fordító figyelmeztetést ad ki.
in
paramétermódosító
A in
módosító szükséges a metódus deklarációjában, de a hívás helyszínén szükségtelen.
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;
}
A in
módosító lehetővé teszi a fordító számára, hogy ideiglenes változót hozzon létre az argumentumhoz, és egy egyszerű hivatkozást adjon át az argumentumra. A fordító mindig létrehoz egy ideiglenes változót, amikor az argumentumot konvertálni kell, amikor implicit átalakítás történik az argumentumtípusból, vagy ha az argumentum nem változó érték. Ha például az argumentum egy literális érték, vagy egy tulajdonság tartozékából visszaadott érték. Ha az API megköveteli, hogy az argumentumot hivatkozással adja át, válassza a ref readonly
módosító helyett a in
módosítót.
A paraméterekkel in
definiált metódusok teljesítményoptimalizálást érhetnek el. Egyes struct
típusargumentumok mérete nagy lehet, és ha a metódusokat szoros hurkokban vagy kritikus kódútvonalakban hívják meg, a struktúrák másolásának költsége jelentős. A metódusok paramétereket deklarálnak in
, hogy az argumentumok hivatkozással biztonságosan átadhatók legyenek, mert a hívott metódus nem módosítja az argumentum állapotát. Ezeknek az argumentumoknak a hivatkozással történő átadása elkerüli a (potenciálisan) költséges másolatot. Explicit módon adja hozzá a in
módosítót a hívási helyen, hogy az argumentumot hivatkozás, nem érték szerint adja át. Az explicit használatnak in
két hatása van:
in
A hívási hely megadása arra kényszeríti a fordítót, hogy válasszon ki egy egyezőin
paraméterrel definiált metódust. Ellenkező esetben, ha két módszer csak jelenlétébenin
különbözik, az érték szerinti túlterhelés jobb egyezés.- A beállítással
in
deklarálja az argumentumok hivatkozással való átadására vonatkozó szándékát. A használt argumentumnakin
egy közvetlenül hivatkozható helyet kell jelölnie. Ugyanezek az általános szabályokout
ésref
argumentumok érvényesek: Nem használhat állandókat, szokásos tulajdonságokat vagy más értékeket termelő kifejezéseket. Ellenkező esetben a hívási helyen való kihagyásin
tájékoztatja a fordítót, hogy nem baj, ha egy ideiglenes változót hoz létre, amelyet csak olvasható hivatkozással adhat át a metódusnak. A fordító létrehoz egy ideiglenes változót, amely argumentumokkalin
old meg számos korlátozást:- Az ideiglenes változók paraméterként
in
lehetővé teszik a fordítási idő állandóit. - Az ideiglenes változók lehetővé teszik a paraméterek tulajdonságait vagy más kifejezéseit
in
. - Az ideiglenes változók olyan argumentumokat engedélyeznek, ahol implicit átalakítás történt az argumentumtípusról a paramétertípusra.
- Az ideiglenes változók paraméterként
Az összes előző példányban a fordító létrehoz egy ideiglenes változót, amely az állandó, a tulajdonság vagy más kifejezés értékét tárolja.
A következő kód az alábbi szabályokat mutatja be:
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`
Tegyük fel, hogy egy másik metódus is elérhető volt értékalapú argumentumokkal. Az eredmények a következő kódban látható módon változnak:
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`
Az egyetlen metódushívás, ahol az argumentumot hivatkozással adja át, az utolsó.
Feljegyzés
Az előző kód argumentumtípusként használja int
az egyszerűséget. Mivel int
a legtöbb modern gépen nem nagyobb, mint egy hivatkozás, nincs előnye, ha egyetlen int
egyszerű hivatkozást ad át.
params
módosító
A metódusdeklaráció kulcsszója után params
más paraméterek nem engedélyezettek, és a metódusdeklarációkban csak egy params
kulcsszó engedélyezett.
A paraméter deklarált típusának params
gyűjteménytípusnak kell lennie. A felismert gyűjteménytípusok a következők:
- Egydimenziós tömbtípus
T[]
, amely esetben az elem típusa .T
- Egy span típus:
System.Span<T>
System.ReadOnlySpan<T>
Itt az elem típusa a következőT
: .
- A megfelelő elemtípussal rendelkező, akadálymentes létrehozási metódussal rendelkező típus. A létrehozási metódus ugyanazzal az attribútummal van azonosítva, amelyet a gyűjteménykifejezésekhez használnak.
- Olyan szerkezet vagy osztálytípus , amely az alábbiakat valósítja meg System.Collections.Generic.IEnumerable<T> :
- A típushoz tartozik egy konstruktor, amely argumentumok nélkül hívható meg, és a konstruktor legalább olyan elérhető, mint a deklaráló tag.
- A típusnak van egy példánya (nem bővítménye),
Add
ahol:- A metódus egyetlen érték argumentummal hívható meg.
- Ha a metódus általános, a típusargumentumok kikövetkeztethetők az argumentumból.
- A módszer legalább olyan akadálymentes, mint a deklaráló tag. Itt az elem típusa a típus iterációs típusa.
- Felület típusa:
A C# 13 előtt a paraméternek egy egydimenziós tömbnek kell lennie.
Ha paraméterrel rendelkező metódust params
hív meg, a következőt adhatja meg:
- A tömbelemek típusának argumentumainak vesszővel tagolt listája.
- A megadott típusú argumentumok gyűjteménye.
- Nincsenek argumentumok. Ha nem küld argumentumokat, a
params
lista hossza nulla.
Az alábbi példa bemutatja, hogy milyen módokon lehet argumentumokat küldeni egy params
paraméternek.
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[]
*/
A túlterhelés feloldása kétértelműséget okozhat, ha egy params
paraméter argumentuma gyűjteménytípus. Az argumentum gyűjteménytípusának konvertálhatónak kell lennie a paraméter gyűjteménytípusára. Ha a különböző túlterhelések jobb konverziókat biztosítanak az adott paraméterhez, ez a módszer jobb lehet. Ha azonban a params
paraméter argumentuma különálló elemek vagy hiányzik, akkor a különböző params
paramétertípusokkal rendelkező összes túlterhelés megegyezik az adott paraméterrel.
További részletekért tekintse meg a C# nyelvspecifikáció argumentumlistáiról szóló szakaszt. A nyelvi specifikáció a C#-szintaxis és -használat végleges forrása.