2009-05-14 11 views
27

Zastanawiam się, jaki jest preferowany sposób skonstruowania nowego obiektu w C#?Jaki jest preferowany sposób konstruowania obiektów w języku C#? Parametry lub właściwości konstruktora?

Weź klasę Osoba:

public class Person 
{ 
    private string name; 
    private int age; 

    //Omitted.. 
} 

należy utworzyć go użyć:

New Person("name", 24); 

lub

New Person() { Name = "name", Age = 24 }; 

jest to tylko kwestia gustu, czy jest tam dobry powód do korzystania z jednego na drugim?

Mogę sobie wyobrazić, że należy używać tylko wymaganych pól w konstruktorze i pól opcjonalnych nie jako parametrów konstruktora, ale za pomocą właściwości.

Czy mam rację?

+2

To jest faktycznie wersja C# z http://stackoverflow.com/questions/830657 –

Odpowiedz

39

Preferowany sposób zależy od projektu.

Właściwości konstruktora dotyczą elementów, których obiekt wymaga, aby można było je poprawnie skonstruować. Innymi słowy, wszelkie właściwości, które obiekt powinien posiadać, aby zostać zainicjalizowane, muszą znajdować się w konstruktorze (zazwyczaj nie potrzebujesz obiektu częściowo zinicjowanego po wywołaniu konstruktora, chyba że tworzysz wzorzec fabryki lub budowniczego, a konstruktor jest ukryty przed wszystkimi oprócz fabryki/budowniczego).

Intializatory właściwości najlepiej nadają się do dodatkowej konfiguracji po konstruktorze, która jest wymagana przez konkretny przypadek użycia, ale nie jest wymagana, aby obiekt mógł zostać uznany za zainicjowany.

Na przykład możesz mieć obiekt reprezentujący osobę. Osoba musi mieć nazwę i wiek, który ma zostać zainicjowany, ale adres, na którym mieszkają, jest konfiguracją opcjonalną.Tak więc nazwa i wiek są parametrami konstruktora, a adres jest właściwością odczytu/zapisu.

Person johnDoe = new Person("John Doe", 24) { Address = "42 Adams Street" }; 
+0

To bardzo ładne przesłanie, które pomaga rozpoznać, kiedy zastosować co. Dzięki! – Peterdk

+0

Polecam również przeczytanie odpowiedzi Marca Gravella (http://stackoverflow.com/a/863064/23234), która zawiera kilka alternatywnych scenariuszy, które wyglądają na rzeczywiste zastosowania, gdzie bardziej idealny punkt widzenia, który przedstawiłem, nie ma związku z innymi ograniczeniami. –

2

Drugim sposobem jest po prostu cukier syntaktyczny do ręcznego ustawiania właściwości:

Person p = new Person(); 
p.Name = "name"; 
p.Age = 24; 

Ty też nie zależnie od konstruktora, który nie może initalize wszystkie właściwości, które chcesz ustawić.

Jeśli twoja klasa ma konstruktor, który wymaga tych dwóch parametrów, nie masz możliwości bezpośredniego wywołania tego konstruktora.

12

To naprawdę zależy od scenariusza. Podejście własnościowe ma wiele wygody, ponieważ nie trzeba duplikować przypisań w konstruktorze. Ponadto większość scenariuszy wiążących dane, takich jak możliwość tworzenia nowych obiektów, z których zazwyczaj korzystają konstruktor bez parametrów, jest dużą zaletą.

Jednak w przypadku typów niezmiennych podejście konstrukcyjne jest jedyną rozsądną opcją. Interesująco (być może) parametry nazwane/opcjonalne w C# 4.0 pozwalają na coś podobnego do obiektów inicjalizujących dla typów niezmiennych - see here.

Podejście konstruktorów jest również bardzo popularne w przypadku frameworków Inversion of Control, ponieważ wyraźnie reklamuje to, czego ta klasa potrzebuje do działania.

Może być konieczne łączenie i dopasowywanie, ale zazwyczaj więcej stylu właściwości niż styl konstruktora.

+2

Tak - tylko do odczytu pola można ustawić tylko za pomocą kodu konstruktora. – Dario

5

Ustawienie wartości w konstruktorze powoduje, że te właściwości są obowiązkowe, co oznacza, że ​​nie można utworzyć nowej instancji, bez ustawiania tych właściwości. W niektórych sytuacjach jest to korzystne, w innych sytuacjach nie jest to korzystne.

+0

To prawda, chociaż w przypadku struktur nie można oczywiście wprowadzić obowiązkowych ustawień. – Noldorin

+0

Rzeczywiście, ale to dlatego, że struct jest typem wartości i zawsze ma wartość (domyślną), nawet jeśli nie jest zainicjalizowana. –

0

Moje główne rozważania na temat tego pytania to: 1) Ile danych będzie miało instancja instancji, kiedy obiekt zostanie utworzony i 2) Jak hermetyzacja ma być klasy? Jeśli, po ustawieniu, właściwości nie powinny być w stanie się zmienić (przynajmniej z powodu jakichkolwiek zewnętrznych elementów), wówczas użyłbym konstruktora, ponieważ konstruktor może ustawić dowolne właściwości tylko do odczytu, jak również wszelkie właściwości odczytu/zapisu.

Należy również pamiętać, że podobnie jak w przypadku każdej innej metody, Konstruktor może zostać przeciążony, więc można skonfigurować dowolną liczbę sposobów inicjowania tych właściwości.

Generalnie używam konstruktorów, ale zdarzają się sytuacje, w których ręcznie ustawiam właściwości. Użyj właściwego narzędzia, aby rozwiązać problem.

7

Zawsze pracuję na zasadzie, że należy przekazać wartości do konstruktora, który jest obowiązkowy dla tego obiektu, aby istniał w ważnym stanie.

W twoim przykładzie możesz powiedzieć, że nowa osoba nie może istnieć bez wieku, więc powinna zostać przekazana do contruktora.

Jeśli pracujesz w oparciu o to, że obiekt powinien modelować rzeczywisty obiekt pracy, to określa on minimum niezbędne do tego, aby dowolny obiekt był ważny - niezależnie od tego, czy ustawisz te wartości jako wartości domyślne, czy przekazując wartości za pośrednictwem konstruktora.

+1

+1: Obiekty powinny być zawsze pozostawione w poprawnym stanie po zakończeniu budowy i po każdym wywołaniu metody, więc metody nie muszą sprawdzać stanu własnego obiektu przed uruchomieniem, tylko ich argumenty. – palm3D

2

Moim zdaniem powinieneś najpierw zdecydować, co czyni osobę osobą. Jakie właściwości osoby dla obiektu osoby mają być prawidłowo tworzone. Jakie są minimalne wymagania dla osoby.

Zawsze powinien istnieć konstruktor z minimalnymi wymaganiami.

Używałbym tylko inicjalizatorów obiektów dla typów, które nie wymagają tworzenia żadnych właściwości. Stąd domyślny konstruktor.

Wiem, że inicjatory obiektów są bardzo wygodne. Inne mechanizmy mogą również wymagać pustego konstruktora w obiekcie.

Ale nie sądzę, że ma to sens, że można stworzyć Osobę bez imienia.

4

Kilka myśli:

  1. Trzeba publicznych właściwości używać inicjatorów obiektu. Więc jeśli jest coś, czego nie chcesz ujawnić, musisz zainicjować je według parametru konstruktora.

  2. Jeśli zaznaczysz IL, to Inicjator obiektów nie jest "atomowy". Jeśli piszesz kod tak (to nie polecam, to tylko przykład):

    using (p = New Person() {Name = GetName(), Age = GetAge()}) 
    { 
        //blah, blah 
    } 
    

    Jeśli istnieje wyjątek w GetAge(), można utworzyć instancję Person w uszkodzonym stanie. Co gorsza, nigdy nie można wprowadzić zakresu stosowania i ta instancja nie zostanie usunięta, jak można sobie wyobrazić.

+0

Podoba mi się, że zauważyłeś, że nie jest atomowa. To oczywiste, kiedy to widzisz, i nie sądzę, żebym napisał kod, jak to określiłeś, ale gdybym czytał czyjś kod, nie sądzę, żebym go odebrał. –

0

Dla ustawienia właściwości ręcznie, musiałyby one zostać uznane za publiczne, a może chcesz członkowie klasy za prywatne. W takim przypadku konstruktor jest do zrobienia, lub pisać metody, aby uzyskać/ustawić je lub użyć akcesor.

0

Mam tendencję do preferowania bardzo prostego konstruktora z intializatorami właściwości. Uważam, że prowadzi to do bardziej wyraźnego kodu. Jedyne dane, które przekażę konstruktorowi, to informacje, których nie chcę, aby użytkownik mojej klasy zmieniał po utworzeniu klasy.

0

Prawdziwy problem pojawia się, gdy przekazujesz obiekt przez warstwy/warstwy. Na przykład możesz utworzyć obiekt osoby i przejść przez serwis internetowy. Usługa sieciowa w usłudze somepoint próbuje deserializować i próbuje utworzyć instancję, a następnie może wystąpić błąd, jeśli dla konstruktorów wymagany jest parametr! jako webservices najpierw tworzy obiekt, a następnie przypisuje wartości.

Więc wolałbym konstruktora bez parametrów, jeśli jest to datamodel (rodzaj POCO), który musi przejść przez poziomy.

Z innych powodów parametr contructor jest najlepszym sposobem (dla pól obowiązkowych). Zwłaszcza w przypadku udostępniania klasy jako iterface do zewnętrznych obiektów lub zespołów.

Powiązane problemy