Mam abstrakcyjną klasę bazową i chcę zadeklarować pole lub właściwość, która będzie miała inną wartość w każdej klasie, która dziedziczy z tej klasy nadrzędnej.
Chcę zdefiniować go w klasie bazowej, aby móc odwoływać się do niego w metodzie klasy bazowej - na przykład przesłaniając ToString, aby powiedzieć „Ten obiekt jest typu property / field ”. Mam trzy sposoby na zrobienie tego, ale zastanawiałem się - jaki jest najlepszy lub akceptowany sposób zrobienia tego? Pytanie dla początkujących, przepraszam.
Opcja 1:
Użyj właściwości abstrakcyjnej i zastąp ją w klasach dziedziczonych. Korzysta z tego, że jest wymuszony (musisz to zmienić) i jest czysty. Ale wydaje się nieco niewłaściwe zwracanie wartości kodu stałego zamiast hermetyzacji pola i jest to kilka wierszy kodu zamiast tylko. Muszę też zadeklarować ciało dla „zestawu”, ale jest to mniej ważne (i prawdopodobnie jest sposób na uniknięcie tego, czego nie jestem świadomy).
abstract class Father
{
abstract public int MyInt { get; set;}
}
class Son : Father
{
public override int MyInt
{
get { return 1; }
set { }
}
}
Opcja 2
Mogę zadeklarować pole publiczne (lub pole chronione) i jawnie nadpisać je w dziedziczonej klasie. Poniższy przykład ostrzega mnie przed użyciem słowa „nowy” i prawdopodobnie mogę to zrobić, ale wydaje mi się to złe i łamie polimorfizm, o który chodziło. To nie jest dobry pomysł ...
abstract class Mother
{
public int MyInt = 0;
}
class Daughter : Mother
{
public int MyInt = 1;
}
Opcja 3
Mogę użyć chronionego pola i ustawić wartość w konstruktorze. Wydaje się to całkiem uporządkowane, ale polega na tym, że konstruktor zawsze to ustawia, a przy wielu przeciążonych konstruktorach zawsze istnieje szansa, że jakaś ścieżka kodu nie ustawi wartości.
abstract class Aunt
{
protected int MyInt;
}
class Niece : Aunt
{
public Niece()
{
MyInt = 1;
}
}
To trochę teoretyczne pytanie i wydaje mi się, że odpowiedzią musi być opcja 1, ponieważ jest to jedyna bezpieczna opcja, ale dopiero zaczynam uporać się z C # i chciałem zapytać o to osoby z większym doświadczeniem.
źródło
Odpowiedzi:
Z trzech rozwiązań tylko opcja 1 jest polimorficzna .
Same pola nie mogą zostać nadpisane. Właśnie dlatego opcja 2 zwraca nowe ostrzeżenie dotyczące słowa kluczowego.
Rozwiązaniem problemu nie jest dodanie słowa kluczowego „nowe”, ale wdrożenie Opcji 1.
Jeśli chcesz, aby twoje pole było polimorficzne, musisz owinąć je we właściwości.
Opcja 3 jest OK, jeśli nie potrzebujesz zachowania polimorficznego. Należy jednak pamiętać, że gdy w czasie wykonywania uzyskuje się dostęp do właściwości MyInt, klasa pochodna nie ma kontroli nad zwracaną wartością. Sama klasa bazowa jest w stanie zwrócić tę wartość.
Tak może wyglądać prawdziwie polimorficzna implementacja twojej właściwości, pozwalająca na kontrolę klas pochodnych .
źródło
Opcja 2 nie jest początkowa - nie możesz nadpisywać pól, możesz je tylko ukryć .
Osobiście za każdym razem wybrałbym opcję 1. Staram się, aby pola były zawsze prywatne. Dzieje się tak, jeśli naprawdę musisz w ogóle mieć możliwość nadpisania właściwości. Inną opcją jest posiadanie właściwości tylko do odczytu w klasie bazowej, która jest ustawiana za pomocą parametru konstruktora:
To prawdopodobnie najbardziej odpowiednie podejście, jeśli wartość nie zmienia się przez cały okres istnienia instancji.
źródło
opcja 2 to zły pomysł. Spowoduje to coś, co nazywa się shadowing; Zasadniczo masz dwóch różnych członków „MyInt”, jednego w matce, a drugiego w córce. Problem polega na tym, że metody zaimplementowane w matce będą odwoływać się do „MyInt” matki, podczas gdy metody zaimplementowane w córce będą odwoływać się do „MyInt” córki. może to spowodować poważne problemy z czytelnością, a później zamieszanie.
Osobiście uważam, że najlepszą opcją jest 3; ponieważ zapewnia wyraźną scentralizowaną wartość i może być wewnętrznie przywoływana przez dzieci bez konieczności definiowania własnych pól - co jest problemem w przypadku opcji 1.
źródło
Możesz to zrobić
virtual pozwala uzyskać właściwość, która coś robi i nadal pozwala podklasom zastąpić to.
źródło
Możesz zdefiniować coś takiego:
Ustawiając wartość jako tylko do odczytu, zapewnia, że wartość dla tej klasy pozostanie niezmieniona przez cały okres istnienia obiektu.
Przypuszczam, że następne pytanie brzmi: dlaczego tego potrzebujesz?
źródło
Jeśli tworzysz klasę i chcesz, aby dla właściwości istniała wartość podstawowa, użyj
virtual
słowa kluczowego w klasie bazowej. Pozwala to opcjonalnie zastąpić właściwość.Korzystając z powyższego przykładu:
W programie:
źródło
Wybrałbym opcję 3, ale mam abstrakcyjną metodę setMyInt, którą podklasy są zmuszone do implementacji. W ten sposób nie będziesz mieć problemu z zapominaniem klasy pochodnej o ustawieniu jej w konstruktorze.
Przy okazji, z opcją pierwszą, jeśli nie określisz set; we właściwości abstrakcyjnej klasy bazowej, klasa pochodna nie będzie musiała jej implementować.
źródło
Możesz skorzystać z opcji 3, jeśli zmodyfikujesz abstrakcyjną klasę bazową, aby wymagała wartości właściwości w konstruktorze, nie przegapisz żadnych ścieżek. Naprawdę rozważyłbym tę opcję.
Oczywiście nadal możesz ustawić pole jako prywatne, a następnie, w zależności od potrzeb, ujawnić obiekt pobierający chronioną lub publiczną własność.
źródło
Ja to zrobiłem...
W ten sposób nadal możesz używać pól.
źródło
Przykładowa implementacja, gdy chcesz mieć klasę abstrakcyjną z implementacją. Podklasy muszą:
W takim przypadku właściwości, które są niezbędne do implementacji, nie powinny być dostępne do użytku, z wyjątkiem klasy abstrakcyjnej i jej własnej podklasy.
źródło