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 pomocí
ref =
, 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.
- Pokud metoda přiřadí parametr odkazující na jiný objekt pomocí
- 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
Bezpečný kontext 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.ref readonly
: 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 nain
parametry.
Členové třídy nemohou mít podpisy, které se liší pouze pomocí ref
, ref readonly
, in
nebo 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á out
parametr , ref readonly
nebo 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í, in
ref
, ref readonly
a 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
ref
ref
musí obsahovat modifikátor. - Argument parametru
out
out
musí obsahovat modifikátor. - Argument parametru
in
může volitelně obsahovatin
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ďin
ref
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.
V následujících typech metod nemůžete použít předchozí modifikátory parametrů:
- Asynchronní metody, které definujete pomocí modifikátoru async
- Metody iterátoru , které zahrnují výnosové vrácení nebo
yield break
příkaz.
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 ain
klíčová slova nelze použít, pokud není prvním argumentemstruct
. - Klíčová
ref readonly
slova ain
klíčová slova nelze použít u žádného obecného typu, i když je omezena na strukturu.
Vlastnosti nejsou proměnné. Jsou to metody. Vlastnosti nemohou být argumenty parametrů ref
.
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ímin
parametrem. V opačném případě, pokud se dvě metody liší pouze v přítomnostiin
, podle přetížení hodnoty je lepší shoda. - Zadáním deklarujete
in
svůj záměr předat argument odkazem. Argument použitý sin
musí představovat umístění, na které lze přímo odkazovat. Platí stejná obecná pravidla aout
ref
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.
- Dočasná proměnná umožňuje konstanty v době kompilace jako
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.
Deklarovaný typ parametru params
musí být typ kolekce. Rozpoznané typy kolekcí jsou:
- Jednorozměrný typ
T[]
pole , v takovém případě typ prvku jeT
. - Typ rozsahu:
System.Span<T>
System.ReadOnlySpan<T>
Zde je typT
prvku .
- Typ s přístupnou metodou create s odpovídajícím typem elementu. Metoda create je identifikována pomocí stejného atributu, který se používá pro výrazy kolekce.
- Struktura nebo typ třídy, který implementujeSystem.Collections.Generic.IEnumerable<T>, kde:
- Typ má konstruktor, který lze vyvolat bez argumentů a konstruktor je alespoň tak přístupný jako deklarující člen.
- Typ má instanci (nikoli rozšiřující) metodu
Add
, kde:- Metodu lze vyvolat pomocí argumentu s jednou hodnotou.
- Pokud je metoda obecná, argumenty typu lze odvodit z argumentu.
- Metoda je alespoň tak přístupná jako deklarující člen. Typ elementu je typ iterace typu.
- Typ rozhraní:
Před jazykem C# 13 musí být parametr jednorozměrným polem.
Při volání metody s parametrem params
můžete předat:
- Čárkami oddělený seznam argumentů typu prvků pole.
- Kolekce 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 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[]
*/