Parametry metody

Ve výchozím nastavení se argumenty v jazyce C# předávají funkcím podle hodnoty. To znamená, že se metodě předá kopie proměnné. Pro typy hodnot (struct) se do metody předá kopie hodnoty . U typů odkazu (class) se metodě předá kopie odkazu . Modifikátory parametrů umožňují předávat argumenty odkazem. Následující koncepty vám pomůžou porozumět těmto rozdílům a způsobu použití modifikátorů parametrů:

  • Předání hodnoty znamená předání kopie proměnné metodě.
  • Předání odkazu znamená předání přístupu k proměnné metodě.
  • Proměnná typu odkazu obsahuje odkaz na jeho data.
  • Proměnná typu hodnoty obsahuje data přímo.

Vzhledem k tomu, že struktura je typ hodnoty, metoda přijímá a pracuje s kopií argumentu struktury při předání struktury podle hodnoty metodě. Metoda nemá přístup k původní struktuře ve volající metodě, a proto ji nemůže žádným způsobem změnit. Metoda může změnit pouze kopii.

Instance třídy je odkazový typ , nikoli typ hodnoty. Pokud je typ odkazu předán hodnotou metodě, metoda obdrží kopii odkazu na instanci třídy. Obě proměnné odkazují na stejný objekt. Parametr je kopie odkazu. Volaná metoda nemůže změnit přiřazení instance ve volající metodě. Volaná metoda však může použít kopii odkazu pro přístup k členům instance. Pokud volána metoda změní člen instance, volající metoda také uvidí tyto změny, protože odkazuje na stejnou instanci.

Výstup následujícího příkladu znázorňuje rozdíl. Metoda ClassTaker změní hodnotu willIChange pole, protože metoda používá adresu v parametru k vyhledání zadaného pole instance třídy. Pole willIChange struktury ve volající metodě se nezmění z volání StructTaker , protože hodnota argumentu je kopie samotné struktury, nikoli kopie jeho adresy. StructTaker změní kopii a při dokončení hovoru dojde ke StructTaker ztrátě kopie.

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
*/

Kombinace typu parametru a režimu argumentů

Jak se předá argument a jestli se jedná o typ odkazu nebo typ hodnoty, řídí, jaké změny provedené v argumentu jsou viditelné od volajícího:

  • Když předáte typ hodnoty podle hodnoty:
    • Pokud metoda přiřadí parametr odkazující na jiný objekt, tyto změny nejsou viditelné z volajícího.
    • Pokud metoda upraví stav objektu, na který odkazuje parametr, tyto změny nejsou viditelné z volajícího.
  • Když předáte typ odkazu podle hodnoty:
    • Pokud metoda přiřadí parametr odkazující na jiný objekt, tyto změny nejsou viditelné z volajícího.
    • Pokud metoda upraví stav objektu, na který odkazuje parametr, tyto změny jsou viditelné z volajícího.
  • Když předáte typ hodnoty odkazem:
    • Pokud metoda přiřadí parametr odkazující na jiný objekt, tyto změny nejsou viditelné z volajícího.
    • Pokud metoda upraví stav objektu, na který odkazuje parametr, tyto změny jsou viditelné z volajícího.
  • Když předáte typ odkazu podle odkazu:
    • Pokud metoda přiřadí parametr odkazující na jiný objekt, tyto změny jsou viditelné z volajícího.
    • Pokud metoda upraví stav objektu, na který odkazuje parametr, tyto změny jsou viditelné z volajícího.

Předání typu odkazu odkazem pomocí odkazu umožňuje volanou metodu nahradit objekt, na který odkazovací parametr odkazuje ve volajícím. Umístění úložiště objektu se předá metodě jako hodnota referenčního parametru. Pokud změníte hodnotu v umístění úložiště parametru (aby odkaz na nový objekt), změníte také umístění úložiště, na které volající odkazuje. Následující příklad předá instanci typu odkazu jako ref parametr.

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

Sejf kontextu odkazů a hodnot

Metody mohou ukládat hodnoty parametrů v polích. Když jsou parametry předány podle hodnoty, to je obvykle bezpečné. Hodnoty se zkopírují a při uložení do pole jsou dostupné odkazové typy. Předání parametrů odkazem vyžaduje, aby kompilátor definoval, kdy je bezpečné přiřadit odkaz na novou proměnnou. Pro každý výraz kompilátor definuje bezpečný kontext , který ohraničuje přístup k výrazu nebo proměnné. Kompilátor používá dva obory: bezpečný kontext a kontext ref-safe.

  • Bezpečný kontext definuje obor, kde se dá bezpečně přistupovat k libovolnému výrazu.
  • Kontext ref-safe definuje obor, kde lze bezpečně přistupovat k libovolnému výrazu nebo ho upravit.

Neformálně si tyto obory můžete představit jako mechanismus, který zajistí, že váš kód nikdy nebude přistupovat k odkazu nebo ho nebude modifikovat. Odkaz je platný, pokud odkazuje na platný objekt nebo strukturu. Bezpečný kontext definuje, kdy lze proměnnou přiřadit nebo znovu přiřadit. Kontext ref-safe definuje, kdy lze proměnnou přiřadit odkaz nebo znovu přiřadit odkaz. Přiřazení přiřadí proměnnou nové hodnotě; Přiřazení odkazu přiřadí proměnnou, která odkazuje na jiné umístění úložiště.

Parametry odkazu

Pro deklaraci parametru použijete jeden z následujících modifikátorů, který předá argumenty odkazem místo hodnoty:

  • ref: Argument musí být inicializován před voláním metody. Metoda může parametru přiřadit novou hodnotu, ale není k tomu nutná.
  • out: Volající metoda není nutná k inicializaci argumentu před voláním metody. Metoda musí přiřadit hodnotu parametru.
  • readonly ref: Argument musí být inicializován před voláním metody. Metoda nemůže k parametru přiřadit novou hodnotu.
  • in: Argument musí být inicializován před voláním metody. Metoda nemůže k parametru přiřadit novou hodnotu. Kompilátor může vytvořit dočasnou proměnnou pro uložení kopie argumentu na in parametry.

Členové třídy nemohou mít podpisy, které se liší pouze pomocí ref, ref readonly, innebo out. K chybě kompilátoru dochází v případě, že jediný rozdíl mezi dvěma členy typu je, že jeden z nich má ref parametr a druhý má outparametr , ref readonlynebo in parametr. Metody však mohou být přetíženy, pokud jedna metoda má ref, ref readonly, in, nebo out parametr a druhý má parametr, který je předán hodnotou, jak je znázorněno v následujícím příkladu. V jiných situacích, které vyžadují porovnávání podpisů, jako je skrytí nebo přepsání, inref, ref readonlya out jsou součástí podpisu a vzájemně se neshodují.

Pokud má parametr jeden z předchozích modifikátorů, může mít odpovídající argument kompatibilní modifikátor:

  • Argument parametru refref musí obsahovat modifikátor.
  • Argument parametru outout musí obsahovat modifikátor.
  • Argument parametru in může volitelně obsahovat in modifikátor. ref Pokud je modifikátor použit v argumentu místo toho, kompilátor vydá upozornění.
  • Argument parametru ref readonly by měl obsahovat buď inref modifikátory, ale ne obojí. Pokud není zahrnut ani jeden modifikátor, kompilátor vydá upozornění.

Když použijete tyto modifikátory, popisují způsob použití argumentu:

  • ref znamená, že metoda může číst nebo zapisovat hodnotu argumentu.
  • out znamená, že metoda nastaví hodnotu argumentu.
  • ref readonly znamená, že metoda čte, ale nemůže zapsat hodnotu argumentu. Argument by měl být předán odkazem.
  • in znamená, že metoda čte, ale nemůže zapsat hodnotu argumentu. Argument bude předán odkazem nebo dočasnou proměnnou.

Vlastnosti nejsou proměnné. Jedná se o metody a nelze je předat parametrům ref . V následujících typech metod nemůžete použít předchozí modifikátory parametrů:

Metody rozšíření mají také omezení použití těchto klíčových slov argumentu:

  • Klíčové out slovo nelze použít u prvního argumentu metody rozšíření.
  • Klíčové ref slovo nelze použít u prvního argumentu metody rozšíření, pokud argument není struct, nebo obecný typ není omezen na strukturu.
  • Klíčová ref readonly slova a in klíčová slova nelze použít, pokud není prvním argumentem struct.
  • Klíčová ref readonly slova a in klíčová slova nelze použít u žádného obecného typu, i když je omezena na strukturu.

ref modifikátor parametrů

Pokud chcete použít ref parametr, musí definice metody i volající metoda explicitně použít ref klíčové slovo, jak je znázorněno v následujícím příkladu. (Kromě toho, že volající metoda může vynechat ref při volání COM.)

void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

Před předáním argumentu parametru ref musí být inicializován.

out modifikátor parametrů

Pokud chcete použít out parametr, musí definice metody i volající metoda explicitně použít out klíčové slovo. Příklad:

int initializeInMethod;
OutArgExample(out initializeInMethod);
Console.WriteLine(initializeInMethod);     // value is now 44

void OutArgExample(out int number)
{
    number = 44;
}

Proměnné předané jako out argumenty nemusí být inicializovány před předáním volání metody. Volaná metoda je však nutná k přiřazení hodnoty před vrácením metody.

Dekonstrukční metody deklarují své parametry pomocí modifikátoru out pro vrácení více hodnot. Jiné metody mohou vrátit řazené kolekce členů hodnot pro více vrácených hodnot.

Před předáním proměnné jako argumentu můžete deklarovat proměnnou v samostatném out příkazu. Můžete také deklarovat out proměnnou v seznamu argumentů volání metody, nikoli v samostatné deklaraci proměnné. out Deklarace proměnných vytvářejí kompaktnější, čitelný kód a také vám brání neúmyslně přiřazovat hodnotu proměnné před voláním metody. Následující příklad definuje proměnnou number ve volání Int32.TryParse metoda.

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

Můžete také deklarovat implicitně zatypovanou místní proměnnou.

ref readonly Modifikátor

ref readonly Modifikátor musí být obsažen v deklaraci metody. Modifikátor v lokalitě volání je volitelný. in Lze použít buď modifikátor, nebo ref modifikátor. ref readonly Modifikátor není platný na webu volání. Který modifikátor, který použijete na webu volání, může pomoct popsat charakteristiky argumentu. Můžete použít ref pouze v případě, že je argument proměnnou a je zapisovatelný. Argument lze použít in pouze v případech, kdy je argument proměnnou. Může být zapisovatelný nebo jen pro čtení. Pokud argument není proměnnou, nemůžete přidat ani modifikátor, ale jedná se o výraz. Následující příklady ukazují tyto podmínky. Následující metoda používá ref readonly modifikátor k označení, že velká struktura by měla být předána odkazem z důvodů výkonu:

public static void ForceByRef(ref readonly OptionStruct thing)
{
    // elided
}

Metodu můžete volat pomocí nebo in modifikátoruref. Pokud modifikátor vynecháte, kompilátor vydá upozornění. Pokud je argument výrazem, nikoli proměnnou, nemůžete přidat in ani ref modifikátory, takže byste měli potlačit upozornění:

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

Pokud je proměnná proměnná proměnná readonly , musíte použít in modifikátor. Kompilátor vydá chybu, pokud místo toho použijete ref modifikátor.

ref readonly Modifikátor označuje, že metoda očekává, že argument bude proměnnou, nikoli výraz, který není proměnnou. Příklady výrazů, které nejsou proměnnými, jsou konstanty, návratové hodnoty metody a vlastnosti. Pokud argument není proměnná, kompilátor vydá upozornění.

in modifikátor parametrů

in Modifikátor je vyžadován v deklaraci metody, ale nepotřebné v lokalitě volání.

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;
}

in Modifikátor umožňuje kompilátoru vytvořit dočasnou proměnnou argumentu a předat do argumentu jen pro čtení odkaz. Kompilátor vždy vytvoří dočasnou proměnnou, pokud je nutné argument převést, pokud existuje implicitní převod z typu argumentu nebo pokud je argument hodnotou, která není proměnnou. Například pokud je argument literálovou hodnotou nebo hodnota vrácená z přístupového objektu vlastnosti. Pokud vaše rozhraní API vyžaduje předání argumentu odkazem, zvolte ref readonly místo modifikátoru in modifikátor.

Metody definované pomocí in parametrů potenciálně získávají optimalizaci výkonu. Některé struct argumenty typu můžou být velké a když se metody volají v těsné smyčce nebo kritické cesty kódu, náklady na kopírování těchto struktur jsou podstatné. Metody deklarují in parametry, které určují, že argumenty lze bezpečně předat odkazem, protože volaná metoda neupravuje stav tohoto argumentu. Předání těchto argumentů odkazem zabrání (potenciálně) nákladné kopii. Modifikátor explicitně přidáte in na web volání, abyste zajistili, že argument bude předán odkazem, nikoli hodnotou. Explicitní použití in má následující dva efekty:

  • Zadání in v lokalitě volání vynutí kompilátor vybrat metodu definovanou s odpovídajícím in parametrem. V opačném případě, pokud se dvě metody liší pouze v přítomnosti in, podle přetížení hodnoty je lepší shoda.
  • Zadáním deklarujete insvůj záměr předat argument odkazem. Argument použitý s in musí představovat umístění, na které lze přímo odkazovat. Platí stejná obecná pravidla a outref argumenty: Nemůžete použít konstanty, běžné vlastnosti ani jiné výrazy, které vytvářejí hodnoty. Jinak vynechání in v lokalitě volání informuje kompilátor, že je v pořádku vytvořit dočasnou proměnnou, která bude předávat odkazem jen pro čtení do metody. Kompilátor vytvoří dočasnou proměnnou pro překonání několika omezení pomocí in argumentů:
    • Dočasná proměnná umožňuje konstanty v době kompilace jako in parametry.
    • Dočasná proměnná umožňuje pro parametry vlastnosti nebo jiné výrazy in .
    • Dočasná proměnná umožňuje argumenty, ve kterých je implicitní převod typu argumentu na typ parametru.

Ve všech předchozích instancích kompilátor vytvoří dočasnou proměnnou, která ukládá hodnotu konstanty, vlastnosti nebo jiného výrazu.

Následující kód ilustruje tato pravidla:

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`

Předpokládejme, že byla k dispozici jiná metoda používající argumenty podle hodnoty. Výsledky se změní, jak je znázorněno v následujícím kódu:

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`

Jediným voláním metody, kde je argument předán odkazem, je poslední volání.

Poznámka:

Předchozí kód se používá int jako typ argumentu pro jednoduchost. Vzhledem k tomu int , že není větší než odkaz ve většině moderních počítačů, neexistuje žádný přínos předání jediného int odkazu jako odkazu jen pro čtení.

params Modifikátor

Za klíčovým slovem v deklaraci metody nejsou povoleny params žádné další parametry a v deklaraci metody je povoleno pouze jedno params klíčové slovo.

Pokud deklarovaný typ params parametru není jednorozměrné pole, dojde k chybě kompilátoru CS0225 .

Při volání metody s parametrem params můžete předat:

  • Čárkami oddělený seznam argumentů typu prvků pole.
  • Pole argumentů zadaného typu.
  • Žádné argumenty. Pokud neodešlete žádné argumenty, délka params seznamu je nula.

Následující příklad ukazuje různé způsoby, jak lze argumenty odeslat do parametru params .

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[]
*/
  • Seznamy argumentů ve specifikaci jazyka C#. Specifikace jazyka je úplným a rozhodujícím zdrojem pro syntaxi a použití jazyka C#.