Jednou z mnoha novinek v C# 4.0 jsou tzv. nepovinné parametry. V tomto článku nebudu popisovat jejich význam či možnosti. Zaměřím se raději na jeden, možná na první pohled ne úplně zřejmý fakt, který by vám mohl možná pěkně zavařit.
reklama
Jednou z mnoha novinek v C# 4.0 jsou tzv. nepovinné parametry. V tomto článku nebudu popisovat jejich význam či možnosti. Zaměřím se raději na jeden, možná na první pohled ne úplně zřejmý fakt, který by vám mohl možná pěkně zavařit.
Nepovinný parametr je jednoduše parametr, který nemusíte přímo naplnit. Podobá se to například několika metodám s různými parametry. Definice je velmi jednoduchá a dobře čitelná:
public static string SaySomething(string s = "rrr")
{
return s;
}
V tomto jednoduchém příkladu je s volitelný parametr. Pokud jej nezadáte, bude jeho hodnota rrr. Vložme tento kód nyní do DLL knihovny a zkompilujme. Pokud se podíváme do výsledného IL kódu, bude vypadat přibližně:
.method public hidebysig static string SaySomething([opt] string s) cil managed
{
.param [1] = "rrr"
// Code size 7 (0x7)
.maxstack 1
.locals init ([0] string CS$1$0000)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret
} // end of method Class1::SaySomething
Na pohled nic zajímavého. Parametr je zde také označen jako volitelný a implicitní hodnota je přítomna. Nebo není? Pokud jste velký geek a po večerech čtete specifikace apod., možná tušíte, kam mířím.
Udělejme novou konzolovou aplikaci s tímto kódem (referencujte v předchozím odstavci vytvořenou knihovnu):
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ClassLibrary1.Class1.SaySomething());
Console.WriteLine(ClassLibrary1.Class1.SaySomething("abcd"));
}
}
Pokud tuto aplikaci spustíte, chová se přesně podle očekávání. Nejprve je vypsán řetězec rrr a poté abcd. Pokud však nyní zkusíte změnit u metody SaySomething změnit hodnotu nepovinného parametru a překompilovat knihovnu bez rekompilace konzolové aplikace, dočkáte se nepříjemného překvapení. Aplikace poskytne stejný výstup jako předtím. Jak je to možné? Odpověď je samozřejmě ve specifikaci resp. pro nás přímo v IL:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 34 (0x22)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "rrr"
IL_0006: call string [ClassLibrary1]ClassLibrary1.Class1::SaySomething(string)
IL_000b: call void [mscorlib]System.Console::WriteLine(string)
IL_0010: nop
IL_0011: ldstr "abcd"
IL_0016: call string [ClassLibrary1]ClassLibrary1.Class1::SaySomething(string)
IL_001b: call void [mscorlib]System.Console::WriteLine(string)
IL_0020: nop
IL_0021: ret
} // end of method Program::Main
Jak vidíte, kód obsahuje hodnotu nepovinného parametru přímo v sobě. Výše ukázaná uložená hodnota v .param, je konstantní hodnota a neuložena v metadatatech (mj. přístupné přes Reflection). Z toho mimo jiné plyne, že volitelný parametr nemůže být např. výsledek volání funkce. Nicméně fakt, že se jedná o konstantu a nikoli např. nějakou instrukci volitelného naplnění parametru, vysvětluje demonstrované chování. Kompilátor totiž tuto hodnotu během kompilace přečte a vloží přímo do výsledného kódu, tak jak je vidět výše.
Pokud tedy hodláte využít volitelných parametrů v nové verzi jazyka C#, je třeba velmi dobře promyslet, zdali se hodnota opravdu nikdy nezmění a je možné ji takto zadat. V opačném případě je třeba vynutit překompilování všech aplikací, které vaši knihovnu využívají. Nebo použít „staré dobré“ overload metody.