C#, tworzenie obiektu klasy różnymi sposobami

0

Siema, czym różnią się następujące linijki kodu

1: Zwierze pies;
2: Zwierze pies = new Zwierze();

Zakładając, że "Zwierze" to klasa, a "pies" to nasz obiekt.

Wiem, że w drugim przypadku tworzony jest obiekt "pies" klasy "Zwierze" z wywołaniem domyślnego konstruktora.

Ale tworząc obiekt pies przy pomocy pierwszego przypadku także mam dostęp do metod i składowych klasy, a więc jaka jest różnica ?

Jeszcze jedna linijka kodu:
3: Zwierze pies = new Zwierze();
pies = new Pies();


"Zwierze" - klasa bazowa niech będzie też abstrakcyjna, "pies" - obiekt, "Pies" - klasa wyprowadzona z klasy bazowej "Zwierze"

Czy w tym przypadku "pies" jako referencja do obiektu traci w drugiej linijce swoje właściwości pierwotne i staje się na nowo referencją do obiektu klasy Pies ?
0
RapanTaj77 napisał(a)

Ale tworząc obiekt pies przy pomocy pierwszego przypadku także mam dostęp do metod i składowych klasy, a więc jaka jest różnica ?

Różnica jest taka, że w pierwszym przypadku nic nie zostanie utworzone, odwołanie się do jakiejkolwiek metody obiektu pies rzuci wyjątkiem.

3:

 Zwierze pies = new Zwierze();
pies = new Pies();

"Zwierze" - klasa bazowa niech będzie też abstrakcyjna, "pies" - obiekt, "Pies" - klasa wyprowadzona z klasy bazowej "Zwierze"

Czy w tym przypadku "pies" jako referencja do obiektu traci w drugiej linijce swoje właściwości pierwotne i staje się na nowo referencją do obiektu klasy Pies ?
</quote>
Po pierwsze nie da się tworzyć obiektów klas abstrakcyjnych. Po drugie - jakie "właściwości pierwotne"?

0

Różnica jest taka, że w pierwszym przypadku nic nie zostanie utworzone, odwołanie się do jakiejkolwiek metody obiektu pies rzuci wyjątkiem.

Hmm, ale jest tworzona chyba jakaś referencja dla klasy bazowej i klas z niej wyprowadzonych ?

Zauważyłem, że można zrobić tak, że tworzymy referencję dla klasy bazowej np.

Zwierze pies;

I później możemy z "pies" utwożyć referencję dla innych obiektów wyprowadzonych z klasy "Zwierze", czyli np.
pies = new Owczarek();
gdzie klasa "Owczarek" dziedziczy po klasie Zwierze.

Oczywiście piszę, że zauważyłem, bo nie wiem czy jest to poprawne ze składnią języka, ale działa :) Proszę o ewentualną poprawę... Czy jest to poprawne rozumowanie ?

Po pierwsze nie da się tworzyć obiektów klas abstrakcyjnych. Po drugie - jakie "właściwości pierwotne"?

Masz rację, machnąłem się z tą klasą abstrakcyjną. Właściwości pierwotne - mam na myśli składowe i metody klasy, przepraszam za brak zrozumienia moich słów, wiem że potrafię zamotać :)

Pomińmy klasę abstrakcyjną i niech to będzie w taki sposób tworzone:

Zwierze pies = new Zwierze();
pies = new Pies();

Klasa "Pies" dziedziczy z klasy "Zwierze".
Pierwsza linijka tworzy obiekt klasy Zwierze, gdzie pies jest referencją do tego obiektu i ten obiekt istnieje w pamięci, tak ?
Następnie dla referencji o nazwie pies podporządkowujemy nowo tworzony obiekt klasy dziedziczonej o nazwie Pies i teraz wskazujemy na nowy obiekt, ale co się stało ze starym obiektem ? Został usunięty ? Co z nim się stało ? Jak do niego się odwołać ?

0
RapanTaj77 napisał(a)

Hmm, ale jest tworzona chyba jakaś referencja dla klasy bazowej i klas z niej wyprowadzonych ?

Jest utworzona zmienna określonego typu. Ale ona na nic nie wskazuje, nie "kryje się" pod nią żaden obiekt.

Oczywiście piszę, że zauważyłem, bo nie wiem czy jest to poprawne ze składnią języka, ale działa :) Proszę o ewentualną poprawę... Czy jest to poprawne rozumowanie ?

Rzeczy niezgodne ze składnią języka się nie kompilują. :)
Tworzysz zmienną klasy bazowej Zwierzę, więc możesz przypisać do niej obiekt klasy Zwierzę i wszystkich pochodnych.

Pierwsza linijka tworzy obiekt klasy Zwierze, gdzie pies jest referencją do tego obiektu i ten obiekt istnieje w pamięci, tak ?
Następnie dla referencji o nazwie pies podporządkowujemy nowo tworzony obiekt klasy dziedziczonej o nazwie Pies i teraz wskazujemy na nowy obiekt, ale co się stało ze starym obiektem ? Został usunięty ? Co z nim się stało ? Jak do niego się odwołać ?

Do starego obiektu nie da się już odwołać - zastąpiłeś go nowym. Obiekt zostanie usunięty niebawem z pamięci przez Garbage Collectora.

0

Następnie dla referencji o nazwie pies podporządkowujemy nowo tworzony obiekt klasy dziedziczonej o nazwie Pies i teraz wskazujemy na nowy obiekt, ale co się stało ze starym obiektem ? Został usunięty ? Co z nim się stało ? Jak do niego się odwołać ?

Polecam mój artykuł: Garbage Collector

0
somekind napisał(a)

Jest utworzona zmienna określonego typu. Ale ona na nic nie wskazuje, nie "kryje się" pod nią żaden obiekt.

somekind napisał(a)

Tworzysz zmienną klasy bazowej Zwierzę, więc możesz przypisać do niej obiekt klasy Zwierzę i wszystkich pochodnych.

1.
Czyli pisząc:
Zwierze pies;
Tworzę zmienna o nazwie "pies" (klasy) typu "Zwierze".
Póki co jest "puste", a zaczyna ona być referencją podczas tworzenia obiektu, czyli przykładowo:
Zwierze pies = new Zwierze();
Tak ?

2.
Jeżeli tak to w takim przypadku z czego wynika reguła, że ze zmiennych tego typu (np. "pies") można tworzyć obiekty dla klas pochodnych, ale nie można już dla innych. Po prostu taka zasada czy z czegoś to wynika ? (Już piszę o co mi chodzi... ;))

class Zwierze {}
class Ssaki : Zwierze{}
class Gady : Zwierze{}
class Ptaki : Zwierze{}

class Czlowiek {}

i np. mogę zrobić tak:

 Zwierze pies;
pies = new Ssaki();

ale nie mogę zrobić tak:
pies = new Czlowiek();

Niby oczywiste (bo pies wywodzi się z klasy Zwierze), ale dlaczego tak jest ?!
Czy mogę napisać w urojonym pseudo kodzie w mojej głowie coś takiego, że: typy klasy bazowej i z niej wyprowadzonych są tym samym typem ? Czyli że: Zwierze = Ssaki = Gady = Ptaki, jako typ zmiennej ?

3.
Powracając do:

 Zwierze pies;
pies = new Ssaki();

Czy zapis powyższy różni się czymś od poniższego:
Ssaki pies = new Ssaki();
Czy się różni ?! Czy ma to jakieś znaczenie, od której klasy tworzyłem zmienną jeżeli potem przypisuję do niej referencję poprzez tworzenie obiektu jej samej lub klasy z niej pochodnej ?

Ponumerowałem abyśmy mogli się lepiej komunikować :P Jeżeli mogę prosić to prosiłbym o odpowiedzi wg. tych numerków, dziękuje.

Deti napisał(a)

Następnie dla referencji o nazwie pies podporządkowujemy nowo tworzony obiekt klasy dziedziczonej o nazwie Pies i teraz wskazujemy na nowy obiekt, ale co się stało ze starym obiektem ? Został usunięty ? Co z nim się stało ? Jak do niego się odwołać ?

Polecam mój artykuł: Garbage Collector

Dziękuje za linka, zapoznam się z tematem.

0
  1. Tak, po wykonaniu new Zwierze(), czyli wywołaniu konstruktora, zmienna pies zaczyna wskazywać na obiekt w pamięci.
  2. Wynika to ze statycznego i silnego typowania - każda zmienna ma swój typ. (Są też języki dynamicznie/słabo typowane, w których zmienna nie ma typu i można do niej wsadzić wszystko.) Typy pochodne są tak jakby rozszerzeniem typów bazowych o nowe elementy, dlatego też każdy obiekt typu pochodnego może być uznany za obiekt typu bazowego - ma wszystkie jego metody i wlaściwości. (Naprawdę trudno mi to wyjaśnić, chyba lepiej to wyjaśnia Twój przykład na zwierzętach.)
  3. Te zapisy różnią się, bo w pierwszym przypadku pies jest typu Zwierzę, więc będzie udostępniał tylko metody tego typu. W drugim przypadk jest Ssakiem, więc będzie posiadał metody typu bazowego Zwierzę, oraz wszystkie metody Ssaka.
0

ale co się stało ze starym obiektem ?
Zapomnij o nim.

Został usunięty ?
Nie od razu — kiedyś będzie. Najpóźniej w chwili zamykania programu.

Co z nim się stało ?
Został zwrócony garbage collectorowi do usunięcia. GC pracuje w tle, odpalając się co jakiś czas, i zwalnia pamięć po takich obiektach.

Jak do niego się odwołać ?
Wcale.

0

Dzięki panowie za odpowiedzi w moim temacie!! :)

Jeszcze jedna sprawa jest dla mnie nie zrozumiała:

somekind napisał(a)
  1. Te zapisy różnią się, bo w pierwszym przypadku pies jest typu Zwierzę, więc będzie udostępniał tylko metody tego typu. W drugim przypadk jest Ssakiem, więc będzie posiadał metody typu bazowego Zwierzę, oraz wszystkie metody Ssaka.

Sprawdziłem i rzeczywiście masz rację, ale w takim przypadku po co robić takie coś:

Zwierze pies = new Ssaki();

(gdzie Ssaki jest klasą wyprowadzoną z Zwierze)

Skoro i tak zmienna pies będzie posiadała jedynie dostęp do składowych i metod klasy bazowej Zwierze.

Kiedy i w jakim celu to się stosuję ? Jakie są tego zalety ? Czy to ma jakikolwiek sens ?

0

Tak, ma sens. Dzięki temu możesz zrobić sobie tablicę Zwierzę[], wsadzić do niej Ssaki, Ptaki i Płazy, a potem w pętli na każdym obiekcie wywołać metodę DajGłos() (zakładamy, że w klasie Zwierze była ona zdefiniowana jako abstrakcyjna, a w klasach pochodnych będzie odpowiednio zdefiniowana) i ptaszki zaćwierkają, pieski zaszczekają, a żaby zakumkają. Nie będziesz potrzebował oddzielnej tablicy dla każdego konkretnego typu, a jedną typu bazowego dla wszystkich obiektów.

0
somekind napisał(a)

Tak, ma sens. Dzięki temu możesz zrobić sobie tablicę Zwierzę[], wsadzić do niej Ssaki, Ptaki i Płazy, a potem w pętli na każdym obiekcie wywołać metodę DajGłos() (zakładamy, że w klasie Zwierze była ona zdefiniowana jako abstrakcyjna, a w klasach pochodnych będzie odpowiednio zdefiniowana) i ptaszki zaćwierkają, pieski zaszczekają, a żaby zakumkają. Nie będziesz potrzebował oddzielnej tablicy dla każdego konkretnego typu, a jedną typu bazowego dla wszystkich obiektów.

Napisałem taki oto kod, czy to miałeś na myśli pisząc powyższego posta ?

    abstract class Zwierze 
    {
        public abstract void DajGlos();
    }

    class Ssaki : Zwierze 
    {
        public override void DajGlos()
        {
            Console.WriteLine("Ssak wydaje dzwiek!");
        }
    }

    class Ptaki : Zwierze
    {
        public override void DajGlos()
        {
            Console.WriteLine("Ptak wydaje dzwiek!");
        }
    }

    class Plazy : Zwierze
    {
        public override void DajGlos()
        {
            Console.WriteLine("Plaz wydaje dzwiek!");
        }
    }

    class Pokaz
    {
        public static void Main()
        {
            Zwierze[] zwierzak = new Zwierze[3];
            zwierzak[0] = new Ssaki();
            zwierzak[1] = new Ptaki();
            zwierzak[2] = new Plazy();

            zwierzak[0].DajGlos();
            zwierzak[1].DajGlos();
            zwierzak[2].DajGlos();

            Console.ReadKey();
        }            
    }

No ok, niech będzie takie wykorzystanie klasy bazowej, niby jakoś może upraszcza kod. Ale czy uproszczenie kodu jest jedyną zaletą utworzenia takiej tablicy ? Kiedy stosować w innych przypadkach ?

Potrafię napisać kod, ale nadal nie rozumiem idei wywoływania zmiennej typu klasy bazowej innych obiektów klas, co jest żałosne... jestem do d**y :P

Czyli nadal nie rozumiem dlaczego

Zwierze zwierzak = new Ssaki();

a nie

Ssaki zwierzak = new Ssaki();

Jakie wady, różnice i zalety ?!

Nie brałem udziału w żadnych nawet małych projektach programistycznych dlatego te moje wątpliwości o przydatność i zastosowanie...

Dziękuje za wyrozumiałość i cierpliwość :)

0
RapanTaj77 napisał(a)

Napisałem taki oto kod, czy to miałeś na myśli pisząc powyższego posta ?

Prawie o to, z małą różnicą. Zamiast:

zwierzak[0].DajGlos();
zwierzak[1].DajGlos();
zwierzak[2].DajGlos();

chodziło mi o:

for(int i = 0; i < zwierzak.Length; i++)
{
    zwierzak[i].DajGlos();
}

No ok, niech będzie takie wykorzystanie klasy bazowej, niby jakoś może upraszcza kod. Ale czy uproszczenie kodu jest jedyną zaletą utworzenia takiej tablicy ? Kiedy stosować w innych przypadkach ?

Upraszczanie kodu jest właściwie celem życia, a nie jedyną zaletą. ;)

Wyobraź sobie okienko zwykłego programu, na nim są różne kontrolki: przyciski, comboboxy, gridy, itd. Okienko nie wie, że przycisk ma wyglądać jak przycisk a combobox jak combobox. Za to wszystkie te kontrolki mają swoją klasę bazową z abstrakcyjną metodą Paint(). Okienko ma tylko tablicę swoich kontrolek, i w pętli wywołuje ich metodę Paint. Każda kontrolka sama wie jakie piksele i gdzie na ekranie ma wyświetlić. Gdy stworzysz swoją nowa kontrolkę, bez problemu wstawisz ją na istniejące okienko, o ile tylko będzie miała tę samą klasę bazową i odpowiednio nadpisaną metodę Paint.

Jakie wady, różnice i zalety ?!

Różnica jest taka, że tworząc obiekt konkretnej klasy masz dostęp do wszystkich jej metod. Gdy ich potrzebujesz, to tworzysz obiekt konkretnej klasy. A jeśli nie, to tworzysz obiekt klasy bazowej.

Nie brałem udziału w żadnych nawet małych projektach programistycznych dlatego te moje wątpliwości o przydatność i zastosowanie...

To chyba warto, żebyś coś napisał i sam poczuł o co chodzi. :)

0

@somekind: Dziękuje, bardzo ładnie mi to napisałeś. Piszę dopiero dziś, bo byłem troszkę zajęty innymi sprawami :) Dzięki jeszcze raz!

1 użytkowników online, w tym zalogowanych: 0, gości: 1