W C# 3.0 wprowadzono nowy typ inicjalizacji pól klas, przykład:
class SampleClass { public int X { get; set; } public int Y { get; set; } } SampleClass sampleClass = null; try { sampleClass = new SampleClass {X = 5, Y = 10}; } catch (Exception) { } int y = sampleClass.Y;
Przed c# 3.0 programiści zwykle używali następującej konstrukcji:
SampleClass sampleClass = null; try { sampleClass=new SampleClass(); sampleClass.X = 5; sampleClass.Y = 10; } catch (Exception) { } int y = sampleClass.Y;
Czy obydwie konstrukcje pełnią taką samą rolę? W końcu zarówno pierwszy jak i drugi sposób służy do inicjalizacji pól. Jeśli zajrzymy do zdekompilowanewgo kodu (np. za pomocą .NET Reflector) to zobaczymy, że w pewnych przypadkach konstrukcje znaczącą będą różnić się zachowaniem. Przyjrzyjmy się najpierw klasycznemu podejściu (druga konstrukcja, kod po dekompilacji):
// kod po dekompilacji, zwykle przypisanie wlasciwosci SampleClass sampleClass; int y; sampleClass = null; Label_0018: try { sampleClass = new SampleClass(); sampleClass.X = 5; sampleClass.Y = 10; goto Label_0038; } catch (Exception) { Label_0033: goto Label_0038; } Label_0038: y = sampleClass.Y;
Nie ma chyba tutaj zaskoczenia. Wywołujemy konstruktor a następnie ustawiamy właściwości X oraz Y (za pomocą wygenerowanych setterów). Sprawdźmy co zostanie wygenerowane jeśli użyjemy inicjalizatora obiektu (pierwsza konstrukcja):
// kod po dekompilacji, inicjalizator obiektow SampleClass sampleClass; SampleClass <>g__initLocal0; int y; sampleClass = null; Label_0018: try { <>g__initLocal0 = new SampleClass(); <>g__initLocal0.X = 5; <>g__initLocal0.Y = 10; sampleClass = <>g__initLocal0; goto Label_003A; } catch (Exception) { Label_0035: goto Label_003A; } Label_003A: y = sampleClass.Y;
Jak widać po dekompilacji zwykłe przypisania wygenerowały inny kod niż inicjalizator obiektu. Jaki to ma skutek w praktyce? Przypuśćmy, że przypisanie wartości do Y, spowoduje wyjątek. W przypadku inicjalizatora obiektu sampleClass będzie miał wartość NULL ponieważ nigdy nie dojdzie do przypisania <>g_initLocal0!
Dla konstrukcji ze zwykłym przypisaniem właściwości, sampleClass będzie różny od NULL, a właściwość X będzie zainicjalizowana (Y będzie pusty, ponieważ wywołał wyjątek).
Decydując się na któreś z rozwiązań warto zwrócić na to uwagę, ponieważ wyjątki typu NULL Reference, są zwykle trudne w znalezieniu. Aby się przekonać, ,że sampleClass naprawdę będzie NULL’em wystarczy zasymulować wyrzucenie wyjątku:
class SampleClass { public int X { get; set; } public int Y { get { return 0; } set{throw new NotImplementedException();} } }