14.11 C#

W języku C# klasy tworzy się podobnie jak w Javie, choć są elementy bardziej przypominające C++. Należy do nich pojęcie struktury.

Tak jak w C++ i Javie „normalne” klasy definiuje się używając słowa kluczowego class.

Można jednak użyć również słowa struct. W Javie jest to niedopuszczalne, w C++ w zasadzie równoważne użyciu class. W C# natomiast obiekty struktur i klas są zupełnie innego typu obiektami. Obiekty klas powstają na stercie (w pamięci wolnej), a deklarowana nazwa (poprzedzająca słowo kluczowe new) jest nazwą nie obiektu, ale odniesienia do obiektu (tak jak w Javie). Natomiast obiekty struktur tworzone są na stosie, tak jak zwykłe lokalne zmienne typów wbudowanych i tak jak w C++ obiekty klas i struktur które tworzymy na stosie (a nie na stercie za pomocą new). W C# nawet jeśli użyjemy new obiekt struktury (ale nie klasy) tworzony jest na stosie. Obowiązuje dla takich obiektów „semantyka wartości”, co w szczególności oznacza, że nazwa może być traktowana jako nazwa obiektu, a nie odnośnika.

Ma to daleko idące konsekwencje. Rozpatrzmy przykład:


P113: struc.cs     Klasy i struktury w C#

      1.  using System;
      2.  
      3.  class OSOBA {
      4.      public string nazwisko;
      5.      public int    wiek;
      6.      public OSOBA(string n, int w) {
      7.          nazwisko = n;
      8.          wiek     = w;
      9.      }
     10.  }
     11.  
     12.  struct Osoba {
     13.      public string nazwisko;
     14.      public int    wiek;
     15.      public Osoba(string n, int w) {
     16.          nazwisko = n;
     17.          wiek     = w;
     18.      }
     19.  }
     20.  
     21.  class StruClass {
     22.      public static void Main() {
     23.          OSOBA kJanek = new OSOBA("Janek",18);
     24.          OSOBA kPiotr = new OSOBA("Piotr",19);
     25.  
     26.          Console.WriteLine("Po utworzeniu obiektow " +
     27.              "klasy:\n" +
     28.              "Janek: " + kJanek.nazwisko + " " +
     29.              kJanek.wiek + " : " + "Piotr: " +
     30.              kPiotr.nazwisko + " " + kPiotr.wiek);
     31.  
     32.          kJanek = kPiotr;
     33.          kPiotr.wiek = 29;
     34.  
     35.          Console.WriteLine("Po przypisaniu i zmianie " +
     36.              "wieku Piotra:\n" +
     37.              "Janek: " + kJanek.nazwisko + " " +
     38.              kJanek.wiek + " : " + "Piotr: " +
     39.              kPiotr.nazwisko + " " + kPiotr.wiek + "\n");
     40.  
     41.          Osoba sJanek = new Osoba("Janek",18);
     42.          Osoba sPiotr = new Osoba("Piotr",19);
     43.  
     44.          Console.WriteLine("Po utworzeniu obiektow " +
     45.              "struktury:\n" +
     46.              "Janek: " + sJanek.nazwisko + " " +
     47.              sJanek.wiek + " : " + "Piotr: " +
     48.              sPiotr.nazwisko + " " + sPiotr.wiek);
     49.  
     50.          sJanek = sPiotr;
     51.          sPiotr.wiek = 29;
     52.  
     53.          Console.WriteLine("Po przypisaniu i zmianie " +
     54.              "wieku Piotra:\n" +
     55.              "Janek: " + sJanek.nazwisko + " " +
     56.              sJanek.wiek + " : " + "Piotr: " +
     57.              sPiotr.nazwisko + " " + sPiotr.wiek + "\n");
     58.      }
     59.  }

Mamy tu klasę OSOBAstrukturę Osoba: jak widać z ich definicji na początku programu (linie 3-19), są one właściwie identyczne. Przyjrzyjmy się wydrukowi tego programu
    Po utworzeniu obiektow klasy:
    Janek: Janek 18 : Piotr: Piotr 19
    Po przypisaniu i zmianie wieku Piotra:
    Janek: Piotr 29 : Piotr: Piotr 29

    Po utworzeniu obiektow struktury:
    Janek: Janek 18 : Piotr: Piotr 19
    Po przypisaniu i zmianie wieku Piotra:
    Janek: Piotr 19 : Piotr: Piotr 29
W liniach 23-24 tworzymy dwa obiekty klasy: odnośniki do nich nazywają się kJanekkPiotr. W linii 32 dokonujemy przypisania ' kJanek = kPiotr;' po czym zmieniamy wiek Piotra (linia 33). W liniach 35-39 drukujemy informacje o obu obiektach: widzimy, że są one identyczne. Zmieniliśmy wiek Piotra, ale wiek Janka też się przy tym zmienił! Takie zachowanie znamy z Javy. Przyczyną tego jest to, że przypisanie ' kJanek = kPiotr;' przepisało odniesienie do obiektu opisującego Piotra do odnośnika kJanek. Teraz wartością obu odnośników, kJanekkPiotr, jest odniesienie do tego samego obiektu. Obiekt pierwotnie wskazywany przez odnośnik kJanek został „zgubiony” i może być usunięty przez odśmiecacz.

Linie 41-57 są powtórzeniem linii 23-39 z tą różnicą, że teraz tworzymy nie obiekty klasy, ale obiekty struktury. Jak widzimy, przypisanie ' sJanek = sPiotr;' spowodowało przepisanie informacji zawartej w obiekcie sPiotr do obiektu sJanek, ale nie utożsamiło tych dwóch obiektów. Zmiana wieku Piotra w linii 51 zmodyfikowała tylko obiekt sPiotr, ale nie sJanek, co widzimy z wydruku: obiekt sJanek zawiera informacje takie jakie zostały tam wpisane za pomocą przypisania ' sJanek = sPiotr;'. Wiek Piotra został potem zmieniony, ale nie miało to już wpływu na wiek zapisany w obiekcie kJanek. Tak więc w tym przypadku mamy cały czas do czynienia z dwoma obiektami, a nazwy sPiotrsJanek są nazwami tych obiektów, a nie odnośników do nich.

Podsumowując

Java.
Struktur nie ma. Obiekty klas tworzone są zawsze na stercie. Obiekty są zawsze anonimowe — dostęp do nich mamy wyłącznie poprzez odniesienia.
C++.
Struktury i klasy są w zasadzie równoważne (z wyjątkiem konwencji dotyczących domyślnego poziomu dostępności). Obiekty mogą być tworzone na stosie (wtedy obowiązuje dla nich semantyka wartości) albo na stercie za pomocą operatora new. W tym drugim przypadku obiekty są anonimowe, a dostęp do nich mamy poprzez wskaźniki.
C#.
Dla struktur obowiązuje zawsze semantyka wartości. Tworzone są na stosie, niezależnie od tego czy użyto operatora new czy nie.
Obiekty klas tworzone są, jak w Javie, zawsze na stercie i dostęp do nich mamy poprzez odnośniki (referencje).

Oczywiście klasy, struktury, konstruktory, tworzenie obiektów itd. to tematy bardzo obszerne i każdy język obiektowy ma mnóstwo specyficznych cech. Ponieważ naszym właściwym celem jest poznanie języka C++, więc nie będziemy tu szerzej omawiać szczegółowo różnic pomiędzy C# a Javą i C++, poprzestając na zasygnalizowaniu tylko niektórych z nich.

T.R. Werner, 21 lutego 2016; 20:17