Use object initializer

Piotr Zieliński pisze ciekawy cykl „Dobre i złe praktyki w C#”, a ja dzisiaj chciałem się odnieść do części VI i opisać rzecz, której dotychczas nie byłem świadom.

Inicjalizacja właściwości obiektu

Kod

var myClass = new MyClass();
myClass.X = 1;

oraz

var myClass = new MyClass {X = 1};

to wbrew pozorom nie to samo zapisane inną składnią. Różnice jednak są.

Gdyby definicja klasy wygląda następująco, tzn. był rzucany wyjątek przy próbie ustawienia właściwości X,

    public class MyClass
    {
        public int X
        {
            get { return 1; }
            set { throw new NotImplementedException(); }
        }
    }

to w pierwszym przypadku myClass będzie w bloku catch obiektem klasy MyClass.

            MyClass myClass = null;
            try
            {
                myClass = new MyClass();
                myClass.X = 1; // rzucenie wyjątku
            }
            catch (Exception)
            {
                Console.WriteLine(myClass == null); // false
            }

a w drugim przypadku myClass będzie ciągle niezainicjalizowane.

            MyClass myClass = null;
            try
            {
                myClass = new MyClass { X = 1 }; // rzucenie wyjątku
            }
            catch (Exception)
            {
                Console.WriteLine(myClass == null); // true
            }

Dzieje się tak ponieważ kompilator wygeneruję zmienną tymczasową do której najpierw przypisany zostanie nowy obiekt a następnie ustawiona właściwość. Dopiero gdy to się powiedzie to do myClass przypisana jest tymczasowa zmienna.

Nie toleruj niepewnych danych

Właśnie ten drugi przypadek jest lepszy, ponieważ nie dostajemy połowicznie zainicjalizowanego obiektu. Wyjątek oczywiście poleciał, ale nie wiem gdzie został przechwycony i jak został obsłużony. W produkcyjnym kodzie nie okraszam wszystkiego try/catchami. Dobrą strategią jest nietolerowanie częściowo zepsutych danych danych i lepiej, żeby był tam null (który ewentualnie się objawi jako NullReferenceException).

Przypadek możliwe, że graniczny, ale ja od teraz będę się starał zawsze korzystać z podpowiedzi Resharpera i skorzystam z „Use object initializer”.
Use object initializer

Reklamy
Ten wpis został opublikowany w kategorii Uncategorized i oznaczony tagami , . Dodaj zakładkę do bezpośredniego odnośnika.

7 odpowiedzi na „Use object initializer

  1. Pingback: dotnetomaniak.pl

  2. burczu pisze:

    Jednak moim zdaniem użycie inicjalizatora nie zawsze jest najlepsze: w przypadku gdy inicjalizujemy w ten sposób obiekt zawierający wiele właściwości, a każdą z właściwości ustawiamy za pomocą różnych metod – w przypadku wyjątku nie wiadomo, która z metod spowodowała wyjątek, a debugowanie samej inicjalizacji jest niemożliwe. No ale to tylko w takich skomplikowanych przypadkach – zwykle też używam inicjalizatorów 😉

    • @burczu „obiekt zawierający wiele właściwości, a każdą z właściwości ustawiamy za pomocą różnych metod ” – to raczej zły design, żeby wykonywać jakąkolwiek logikę w setterach.
      Ja bym to widział tak: Ustawienie kilku pól jest ok, później może przyjść METODA, która jakąś logikę wykona (metody nie wrzucimy jako object initializer). Jest czyściej. Jedyną logikę jaką bym dopuszczał w setterach to coś w stylu:
      if(this.otherField == notSet && value != otherValue)
      {
      throw new InvalidOperationException(string.Format(„{0} and {1} cannot be set together”, this.otherField, value));
      }
      czyli sprawdzenie czy ustawienia nie wpadły w jakąś niedozwoloną konfigurację.

  3. Vibo pisze:

    „w przypadku wyjątku nie wiadomo, która z metod spowodowała wyjątek” – naprawdę nie wiadomo? To po co jest stacktrace?

  4. czy tylko mój Visual 2012 zatrzymuje się na throw new NotImplementedException(); i podkreśla na źółto przytoczony przypadek?

    Może warto włączyć Common Language Runtime Exceptions (check box przy thrown) w Debug->Exceptions (CTRL+ALT+E)

Możliwość komentowania jest wyłączona.