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 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á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

Széf hivatkozások és értékek kontextusa

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.
  • readonly ref: 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étereknek in .

Az osztály tagjai nem rendelkezhetnek olyan aláírásokkal, amelyek csak az ref, , ref readonlyinvagy 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 readonlyvagy in paraméterrel. A metódusok azonban túlterhelhetők, ha az egyik metódus rendelkezik refegy , ref readonly, invagy 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, inrefref readonlyaz 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ét ref .
  • Egy paraméter argumentumának out tartalmaznia kell a out módosító értékét.
  • Egy paraméter argumentuma in opcionálisan tartalmazhatja a in módosító értékét. Ha a ref 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 vagy ref a in 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.

A tulajdonságok nem változók. Metódusok, és nem adható át paramétereknek ref . 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 nem structegy , vagy egy általános típus, amely nem konstruktálásra van korlátozva.
  • A ref readonly kulcsszavak és in a kulcsszavak csak akkor használhatók, ha az első argumentum egy struct.
  • A ref readonly kulcsszavak és in a kulcsszavak nem használhatók általános típuson, még akkor sem, ha a szerkezetre korlátozva van.

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ében inkülönbözik, az érték szerinti túlterhelés jobb egyezés.
  • A beállítással indeklarálja az argumentumok hivatkozással való átadására vonatkozó szándékát. A használt argumentumnak in egy közvetlenül hivatkozható helyet kell jelölnie. Ugyanezek az általános szabályok out és ref 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ás in 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 argumentumokkal in 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 ö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.

Ha a params paraméter deklarált típusa nem egydimenziós tömb, cs0225-ös fordítóhiba lép fel.

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 tömbje.
  • 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 class MyClass
{
    public static void UseParams(params int[] list)
    {
        for (int i = 0; i < list.Length; i++)
        {
            Console.Write(list[i] + " ");
        }
        Console.WriteLine();
    }

    public static void UseParams2(params object[] list)
    {
        for (int i = 0; i < list.Length; i++)
        {
            Console.Write(list[i] + " ");
        }
        Console.WriteLine();
    }

    static void Main()
    {
        // You can send a comma-separated list of arguments of the
        // specified type.
        UseParams(1, 2, 3, 4);
        UseParams2(1, 'a', "test");

        // A params parameter accepts zero or more arguments.
        // The following calling statement displays only a blank line.
        UseParams2();

        // 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 };
        UseParams(myIntArray);

        object[] myObjArray = { 2, 'b', "test", "again" };
        UseParams2(myObjArray);

        // The following call causes a compiler error because the object
        // array cannot be converted into an integer array.
        //UseParams(myObjArray);

        // The following call does not cause an error, but the entire
        // integer array becomes the first element of the params array.
        UseParams2(myIntArray);
    }
}
/*
Output:
    1 2 3 4
    1 a test

    5 6 7 8 9
    2 b test again
    System.Int32[]
*/