Klasy są podstawowymi typami referencyjnymi w C#.
Ogólna deklaracja klasy:
[modyfikatory] class nazwaKlasy [ : nazwaNadklasy , interfejsy ] { // Deklaracje składowych klasy }
Przykładowo:
class Przykład {
int wartość ; // deklaracja składowej
public Przykład ( int wartość ) { // Konstruktor
this.wartość = wartość ;
}
}
Dostępne modyfikatory dla klasy:
public : (ew. internal) modyfikuje dostęp do klasy. Domyślnie klasa jest prywatna.
sealed : powoduje, że z danej klasy nie można dziedziczyć. Np. sealed class A { ... }
abstract : tworzy klasę abstrakcyjną (mogącą zawierać metody abstrakcyjne). Przydatne przy dziedziczeniu.
this : słowo kluczowe oznaczające referencję do obiektu, z którego
wywołano metodę (inaczej mówiąc oznacza referencję do samego siebie). Oczywiście
nie można używać this
wewnątrz metod statycznych (one nie są
wywoływane na rzecz obiektu).
base : słowo kluczowe pozwalające na odwołania do składowych klasy bazowej (oznacza referencję do obiektu klasy bazowej "zaszytego" w obiekcie, z którego wywołano metodę).
Konstruktor jest specjalną metodą (o nazwie identycznej z nazwą klasy) wywoływaną automatycznie podczas tworzenia egzemplarza klasy (ew. struktury). Konstruktor nie posiada typu zwracanej wartości (nie zwraca nawet void). Można przeciążać konstruktory konstruktor nie pobierający żadnego argumentu jest konstruktorem domyślnym. Ponadto jeśli klasa nie definiuje żadnego konstruktora, to automatycznie jest dla niej tworzony konstruktor domyślny (inicjalizujący wszystkie pola domyślną wartością).
Np.
class A { public A() {...} // Deklaracja konstruktora domyślnego }
Konstruktor klasy pochodnej musi najpierw wywołać konstruktor klasy bazowej.
W przypadku gdy klasa bazowa nie posiada konstruktora domyślnego, musimy
jawnie wywołać jeden z jej konstruktorów. W tym celu używa się słowa
kluczowego base
.
Ponadto definiując metodę, możemy wskazać, aby przed jej wykonaniem
został wywołany jeden z konstruktorów (uzyskujemy to poprzez zapisanie po
dwukropku słowa kluczowego this
w formie wywołania metody).
Przykład:
class MyClass : A {
MyClass(int a): base(a){...} // Konstruktor wywołujący konstruktor nadklasy.
MyClass(): this(0){...} // Konstruktor domyślny, wywołujący
} //konstruktor z parametrem.
Konstruktor może posiadać dowolny rodzaj dostępu.
Ponadto możliwe jest tworzenie konstruktorów statycznych (jednak taki konstruktor nie może pobierać żadnych parametrów, np.
class Witacz {
public static Witacz() { System.Console.WriteLine("Witaj");
}
Statyczny konstruktor wykonuje operacje potrzebne do inicjalizacji klasy.
Statyczne konstruktory nie mogą być wywoływane bezpośrednio.
Destruktor jest specjalną metodą (o nazwie ~NazwaKlasy) wywoływaną podczas usuwania obiektu z pamięci. Ma na celu posprzątanie po istnieniu obiektu danej klasy (np. zwolnienie zasobów systemowych). Należy jednak pamiętać, że w C# zwalnianiem nieużywanej pamięci zajmuje się system, a nie programista.
Np.
public class A {
...
~A( ) { ... } // Destruktor
}
Destruktory nie są dziedziczone. Ponadto nie mogą pobierać żadnych parametrów. Nie określamy także zwracanej wartości ani specyfikatora dostępu. Destruktor jest wywoływany tylko automatycznie - nie można nakazać jego wywołania.
Destruktory są implementowane poprzez przeciążenie metody Finalize z klasy System.Object. Programy w C# nie mogą bezpośrednio przeciążać ani wywoływać metody Finalize.
C# pozwala przeciążać operatory. Aby to zrobić definiujemy funkcję statyczną nazywającą się operator XX( ) gdzie w miejsce XX wstawiamy symbol jednego z przeciążalnych operatorów. Operatory, które można przeciążać to:
+ - * / % ! != == ~ << >> < <= > >= & | ^ ++ --
Przykładowo:
class LZespol {
double Re, Im ;
LZespol ( int r, int i ) { Re = r ; Im = i ; } // Konstruktor
public static LZespol operator + ( LZespol a, LZespol b) { // operator dodawania
return new LZespol ( a.Re+ b.Re , a.Im + b.Im );
}
}
Pomimo przeciążenia operatory zachowują swój priorytet (np. zawsze operator mnożenia * ma wyższy priorytet niż operator dodawania). Operatory * oraz & dają się przeciążać tylko w wersji dwuargumentowej.
Poza zwykłymi operatorami możemy także przeciążyć 'operatory'
true
oraz false
.
Operatory && oraz || są automatycznie rozwijane z operatorów & i |, dlatego też nie muszą być przeciążane.
Operatory logiczne muszą być przeciążane 'parami'. To znaczy, że jeśli przeciążymy operator >, to musimy także przeciążyć operator < (pozostałe pary to == i != oraz <= i >= ).