Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

ucel.net

Parametry opcjonalne i nazywane

Używając analizy kodu w projektach .NET 4.0 można natknąć się na taki oto komunikat:

CA1026: Microsoft Design: Replace method xyz with an overload that supplies all default arguments.

W dokumentacji do tego ostrzeżenia czytamy, że choć używanie metod opcjonalnych jest dozwolone w specyfikacji CLS, to dozwolone jest także ich ignorowanie. Przyjrzyjmy się więc temu nieco bliżej.

Parametry opcjonalne

Na tapetę weźmiemy tę oto prostą metodę:
public void OptionalMethod(string name, int value = 42)
{
  Console.WriteLine("OptionalMethod z parametrami");
  Console.WriteLine("Name: {0}", name);
  Console.WriteLine("Value: {0}", value);
  Console.WriteLine();
}

Jej nagłówek w kodzie IL wygląda następująco:

.method public hidebysig instance void OptionalMethod(string name,
            [opt] int32 'value') cil managed
{
 .param [2] = int32(0x0000002A)
 // Code size 48 (0x30)
 .maxstack 8
 IL_0000: nop

Jasno jest oznaczone, że parametr value jest parametrem opcjonalnym i jego wartość domyślna to 42 (0x2A). Co ciekawe, identyczny nagłówek dostaniemy implementując powyższą metodę w Visual Basicu pod kontrolą .NET 2.0. Parametry opcjonalne nie są więc novum w .NET 4.0, a po prostu ich obsługa została dodana do kompilatora C#.

Skąd więc ostrzeżenie? Niestety tak jak w poprzedniej wesji .NET nie dało się użyć parametrów domyślnych metody napisanej w VB w programie napisanym w C#, tak cały czas nie da się ich użyć w programie napisanym np. w Managed C++. Poniższy kod się nie skompiluje - pojawi się błąd mówiący, że nie ma metody OptionalMethod z jednym parametrem:

int main(array ^args)
{
  OptionalClass ^c = gcnew OptionalClass();
  c->OptionalMethod(L"Test");
  return 0;
}

Spowodowane jest to tym, że parametry domyślne nie są obsługiwane przez runtime a bezpośrednio przez kompilator. Informacje z nagłówka metody są używane przez kompilator do wygenerowania poprawnego wywołania metody. Dlatego też poniższy kod:
OptionalClass oc = new OptionalClass();
oc.OptionalMethod("Test1");
oc.OptionalMethod("Test2", 6);

zostanie przetłumaczony następująco:

IL_0001: newobj instance void [Classes]ZineTest.OptionalClass::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: ldstr "Test1"
IL_000d: ldc.i4.s 42
IL_000f: callvirt instance void [Classes]ZineTest.OptionalClass::OptionalMethod(string,int32)
IL_0014: nop
IL_0015: ldloc.0
IL_0016: ldstr "Test2"
IL_001b: ldc.i4.6
IL_001c: callvirt instance void [Classes]ZineTest.OptionalClass::OptionalMethod(string,int32)

Jak więc widać w obu przypadkach przed wywołaniem metody na stosie lądują dwie wartości: łańcuch i liczba. W pierwszym przypadku jest to wartość domyślna, pobrana z nagłówka metody. Kompilator MC++ został niestety potraktowany nieco po macoszemu i parametrów domyślnych .NET nie obsługuje. Może w następnej wersji?

Parametry nazywane

Od wersji 4.0 kompilator C# pozwala na przekazywanie parametrów do metod w dowolnej kolejności. Należy "tylko" podać ich nazwy w wywołaniu metody, jak to zostało pokazane poniżej:

oc.OptionalMethod(value: 12, name:"Test3");
oc.OptionalMethod(name:"Test4", value:666);

Resztę pracy wykona kompilator. Ale jaką resztę dokładnie? I znowu ILDasm prawdę Ci powie. Jak wygląda normalne wywołanie widzieliśmy już wyżej. Teraz kod wygląda nieco inaczej.

IL_0021: nop
IL_0022: ldloc.0
IL_0023: ldc.i4.s 12
IL_0025: stloc.1
IL_0026: ldstr "Test3"
IL_002b: stloc.2
IL_002c: ldloc.2
IL_002d: ldloc.1
IL_002e: callvirt instance void [Classes]ZineTest.OptionalClass::OptionalMethod(string,int32)
IL_0033: nop
IL_0034: ldloc.0
IL_0035: ldstr "Test4"
IL_003a: stloc.2
IL_003b: ldc.i4 0x29a
IL_0040: stloc.1
IL_0041: ldloc.2
IL_0042: ldloc.1
IL_0043: callvirt instance void [Classes]ZineTest.OptionalClass::OptionalMethod(string,int32)

Ups. Dla każdego parametru została wcześniej zadeklarowana zmienna. Kompilator przypisuje zawartość parametrów do odpowiednich zmiennych a następnie na stos ładuje ich wartości - już we właściwej kolejności. Co więcej, czynność ta wykonywana jest nawet gdy kolejność prametrów jest właściwa. Można się sprzeczać, czy jest o co szablę kruszyć. Narzut czasowy jaki udało mi się zmierzyć dla 20 parametrów można praktycznie zignorować. Warto jesdnak jest mieć ten fakt na uwadze, choćby z tego powodu, że gdzieś w pamięci zduplikowane zostaną dane przekazane do metody. A to nie zawsze może być pożądane.

Opublikowane 24 lutego 2011 12:03 przez ucel
Filed under: , ,

Komentarze:

 

dotnetomaniak.pl said:

Dziękujemy za publikację - Trackback z dotnetomaniak.pl

lutego 24, 2011 16:54
 

Alkor Trade said:

Dzieki za podanie kodu

sierpnia 12, 2011 16:13
Komentarze anonimowe wyłączone
W oparciu o Community Server (Personal Edition), Telligent Systems