Podczas definicji klasy, definiujemy swojego rodzaju szkielet dla naszych danych. Szkielet ten nie definiuje jeszcze żadnych danych, definiuje jednak, co oznacza nasza klasa. Jest to zbiór informacji mówiący o tym, z czego składa się nasz obiekt oraz jakie operacje będą mogły być wykonane na tym obiekcie. Obiekt to instancja klasy. Metody i zmienne stanowiące klasę nazywamy składowymi.

Definicja klasy

Definicja klasy zaczyna się od słowa kluczowego class, po którym następuje nazwa klasy. Wnętrze klasy jest otoczone parą nawiasów klamrowych.
Poniżej powszechna definicja klasy:
modyfikator_dostepu class nazwa_klasy
{
    // pola klasy
    modyfikator_dostepu typ_danych zmienna1;
    modyfikator_dostepu typ_danych zmienna2;
    ...
    modyfikator_dostepu typ_danych zmiennaN;
    // metody klasy
    modyfikator_dostepu zwracany_typ metoda1(lista_parametrow)
    {
        wnetrze_metody
    }
    modyfikator_dostepu zwracany_typ metoda2(lista_parametrow)
    {
        wnetrze_metody
    }
    ...
    modyfikator_dostepu zwracany_typ metodaN(lista_parametrow)
    {
        wnetrze_metody
    }
}
gdzie,
  • modyfikator_dostepu określa dostępność samej klasy oraz wszystkich jej składowych. Jeżeli modyfikator dostępu nie będzie podany dla klasy to domyślnie będzie to internal. Domyślny modifykator dostępu dla składowych klasy to private;
  • zwracany_typ określa rodzaj zmiennej, która zostanie zwrócona z metody o ile typ danych będzie zwracany. Jeżeli nie to zwracany typ takiej metody to void;
  • aby uzyskać dostęp do składowej klasy należy posłużyć się operatorem kropki (.);
  • operator kropki (.) łączy nazwę obiektu z nazwą składowej.
Definicja klasy
using System;
namespace Klasy
{
    class Program
    {
        static void Main(string[] args)
        {
            Pudelko pudelko1 = new Pudelko(); // definicja obiektu1 typu Pudelko
            Pudelko pudelko2 = new Pudelko(); // definicja obiektu2 typu Pudelko
            // spefyfikacja 1
            pudelko1.dlugosc = 5.5;
            pudelko1.wysokosc = 6.0;
            pudelko1.szerokosc = 3.0;
            // spefyfikaja 2
            pudelko2.dlugosc = 3.5;
            pudelko2.wysokosc = 2.0;
            pudelko2.szerokosc = 1.0;
            // Objetosc 1
            double obj1 = pudelko1.ObliczObjetosc(pudelko1.dlugosc, pudelko1.szerokosc, pudelko1.wysokosc);
            Console.WriteLine("Objetość pudełka nr 1: {0}", obj1);
            // Objetosc 2
            // Obliczenie bez użycia powyższej metody
            double obj2 = pudelko2.dlugosc * pudelko2.szerokosc * pudelko2.wysokosc;
            Console.WriteLine("Objetość pudełka nr 2: {0}", obj2);
            Console.ReadKey();
            // Wynik działania programu
            //Objetosc pudelka nr 1: 99
            //Objetosc pudelka nr 2: 7
        }
    }
    class Pudelko
    {
        public double dlugosc;
        public double szerokosc;
        public double wysokosc;
        // Metoda do obliczania objetosci zdefinowa jaka skladowa klasy
        public double ObliczObjetosc(double dl, double szer, double wys)
        {
            return dl * szer * wys;
        }
    }
}

Metody klasy oraz hermetyzacja

Metoda klasy, podobnie jak pole klasy, ma swoją definicję. Działa na każdym obiekcie klasy, której jest składową i ma dostęp do wszystkich składowych tej klasy.

Zmienne składowe są atrybutami obiektu (z punkcji widzenia projektowania) i przechowywane są, jako prywatne z punktu widzenia hermetyzacji. Składowe te mogą być dostępne jedynie, jeżeli zostaną opatrzone modyfikatorem dostępu public.

Wszystkie powyższe punkty zdecydowanie łatwiej będzie dostrzeć na przykładzie. Spróbujmy zatem ustawić oraz pobrać wartości od różnych składowych klasy:
using System;
namespace Klasy
{
    class Program
    {
        static void Main(string[] args)
        {
            Pudelko pudelko1 = new Pudelko(); // definicja obiektu1 typu Pudelko
            Pudelko pudelko2 = new Pudelko(); // definicja obiektu2 typu Pudelko
            // spefyfikacja 1
            pudelko1.UstawDlugosc(5.5);
            pudelko1.UstawWysokosc(6.0);
            pudelko1.UstawSzerokosc(3.0);
            // spefyfikaja 2
            pudelko2.UstawDlugosc(3.5);
            pudelko2.UstawWysokosc(2.0);
            pudelko2.UstawSzerokosc(1.0);
            // Objetosc 1
            double obj1 = pudelko1.ObliczObjetosc();
            Console.WriteLine("Objetość pudełka nr 1: {0}", obj1);
            // Objetosc 2
            // Obliczenie bez użycia powyższej metody
            double obj2 = pudelko2.ObliczObjetosc();
            Console.WriteLine("Objetość pudełka nr 2: {0}", obj2);
            Console.ReadKey();
            // Wynik działania programu
            //Objetosc pudelka nr 1: 99
            //Objetosc pudelka nr 2: 7
        }
    }
    class Pudelko
    {
        private double dlugosc; // pole dostepne tylko z wnetrza klasy
        private double szerokosc; // pole dostepne tylko z wnetrza klasy
        private double wysokosc; // pole dostepne tylko z wnetrza klasy
        // definiujemy metody, które mają dostęp do tych pól oraz są publiczne
        public void UstawDlugosc(double dl)
        {
            dlugosc = dl;
        }
        public void UstawSzerokosc(double szer)
        {
            szerokosc = szer;
        }
        public void UstawWysokosc(double wys)
        {
            wysokosc = wys;
        }
        // Metoda do obliczania objetosci w innej postaci niż w pierwszym przykladzie
        // Składowe klasy maja już swoje wartości, mozemy obliczyć objętosc
        public double ObliczObjetosc()
        {
            return dlugosc * szerokosc * wysokosc;
        }
    }
}

Konstruktor klasy

Konstruktor klasy jest specjalną metodą, która jest wykonywana zawsze, kiedy tworzony jest obiekt klasy.

Konstruktor ma dokładnie taką samą nazwę jak nazwa klasy oraz nie zwraca żadnego typu. Poniższa konstrukacja pokazuje przykład użycia konstruktora:
using System;
namespace Klasy
{
    class Program
    {
        static void Main(string[] args)
        {
            // W momencie tworzenia nowego obiektu dojdzie do wywołania konstruktora
            KlasaZKonstruktorem kzk = new KlasaZKonstruktorem();
        
            Console.ReadKey();
        }
    }
    class KlasaZKonstruktorem
    {
        private string tekst;
        public KlasaZKonstruktorem()
        {
            tekst = "Wywołanie konstruktora klasy";
            Console.WriteLine(tekst);
        }
    }
}
Domyślny konstruktor nie ma żadnego parametru, jeżeli jednak zajdzie taka potrzeba przygotowany przez nas konstruktor te parametry może posidać. Konstruktor taki nosi nazwę konstruktora parametryzowanego. Technika taka pozwala na przypisanie wartości początkowej do obiektu w trakcie jego tworzenia co pokazano na poniższym przykładzie:
using System;
namespace Klasy
{
    class Program
    {
        static void Main(string[] args)
        {
            string marka = "";
            Console.Write("Podaj markę samochodu: ");
            marka = Console.ReadLine();
            KonstruktorParametryzowany kp = new KonstruktorParametryzowany(marka);
            Console.ReadKey();
        }
    }
    class KonstruktorParametryzowany
    {
        private string marka;
        public KonstruktorParametryzowany(string parametr)
        {
            marka = parametr;
            Console.WriteLine("Marka podana przez użytkownika to: {0}", marka);
        }
    } 
}

Destruktor klasy

Destruktor jest specjalną metodą klasy, która jest wykonywana przez Garbage Collector. Destruktor ma dokładnie taką samą nazwę jak nazwa klasy oraz przedrostek (~). Destruktor nie może zwracać oraz przyjmować żadnych parametrów.

Destruktor może być bardzo przydatny, gdy chcemy zwolnić zasoby pamięci przed wyjściem z programu. Destruktory nie mogą być dziedziczone ani przeciążone.

using System;
namespace Klasy
{
    class Program
    {
        static void Main(string[] args)
        {
            KlasaZDestruktorem kzd = new KlasaZDestruktorem();
            kzd.PobierzLiczbe();
            kzd.WyswietlLiczbe();
            Console.ReadKey();
            // Wynik działania programu
            //Obiekt jest tworzony
            //Liczba: 100
            //Obiekt jest kasowany
        }
    }
    class KlasaZDestruktorem
    {
        private int liczba;
        // konstruktor
        public KlasaZDestruktorem()
        {
            Console.WriteLine("Obiekt jest tworzony");
        }
        // destruktor
        ~KlasaZDestruktorem()
        {
            Console.WriteLine("Obiekt jest kasowany");
        }
        public void PobierzLiczbe()
        {
            liczba = 100;
        }
        public void WyswietlLiczbe()
        {
            Console.WriteLine("Liczba: {0}", liczba);
        }
    }
}

Statyczne składniki klasy

Składniki statyczne możemy zdefiniować za pomocą słowa kluczowego static. Jeżeli zadeklarujemy składową jako statyczną nie ważne jak wiele obiektów klasy utworzymy, istnieje zawsze tylko jedna kopia składowej statycznej.

Słowo kluczowe static mówi nam o tym, że istnieje tylko jedna instancja składowej dla klasy. Zmienne statyczne używane są przeważnie do definiowania stałych, ponieważ ich wartości mogą być wywołane bez tworzenia instancji danej klasy. Statyczne zmienne mogą być zainicowane na zewnątrz metody lub definicji klasy. Mogą również zostać zainicowane wewnątrz definicji klasy.
Przykład:
using System;
namespace Klasy
{
    class Program
    {
        static void Main(string[] args)
        {
            StatyczneSkladowe ss1 = new StatyczneSkladowe();
            ss1.Dodaj();
            ss1.Dodaj();
            ss1.Dodaj();
            // Teraz utworzymy kolejny obiekt typu StatyczneSkladowe
            StatyczneSkladowe ss2 = new StatyczneSkladowe();
            // Pamiętajcie, że mamy tylko jedną kopię zmiennej statycznej
            ss2.Dodaj();
            ss2.Dodaj();
            ss2.Dodaj();
            Console.WriteLine("Wartość liczby z pierwszego obiektu: {0}", ss1.WyswietlNumer());
            Console.WriteLine("Wartość liczby z drugiego obiektu: {0}", ss2.WyswietlNumer());
            Console.ReadKey();
            // Jeżeli wynik jest dla Ciebie zaskakujący przeczytaj jeszcze raz
            // definicję składowych statycznych
            // Wynik działania programu
            //Wartosc liczby z pierwszego obiektu: 6
            //Wartosc liczby z drugiego obiektu: 6
        }
    }
    class StatyczneSkladowe
    {
        public static int liczba;
        public void Dodaj()
        {
            liczba++;
        }
        public int WyswietlNumer()
        {
            return liczba;
        }
    }
}	
Można również zdefiniować metody klasy jako statyczne. Takie metody mają dostęp tylko do zmiennych statycznych. Funkcje statyczne istnieją nawet zanim obiekt zostanie utworzony. Poniżej przykład użycia statycznych metod:
using System;
namespace Klasy
{
    class Program
    {
        static void Main(string[] args)
        {
            StatycznaMetoda sm = new StatycznaMetoda();
            sm.Dodaj();
            sm.Dodaj();
            sm.Dodaj();
            // Metoda statyczna jest dostępna bez tworzenia obiektu klasy
            Console.WriteLine("Wartość liczby: {0}", StatycznaMetoda.ZwrocLiczbe());
            Console.ReadKey();
            // Wynik działania programu
            //Wartosc liczby: 3
        }
    }
    class StatycznaMetoda
    {
        public static int liczba;
        public int liczba2;
        public void Dodaj()
        {
            liczba++;
        }
        public static int ZwrocLiczbe()
        {
            // w metodzie nie mamy dostępu do zmiennej liczba2
            return liczba;
        }
    }
}

Klasa statyczna

Warto również wiedzieć, że możemy utworzyć statyczną klasę. Klasa taka mówi, iż nie została napisana po to, aby tworzyć nowe obiekty. Nawet, gdybyśmy chcieli utworzyć nowy obiekt klasy statycznej kompilator zgłosi błąd.

W klasie takiej mogą znajdować się tylko statyczne składowe. Prostym przykładem takie klasy może być klasa odpowiedzialna za wykonywanie obliczeń matematycznych. Nie chcemy tworzyć instancji klasy za każdym razem, kiedy chcemy wykonać dodawanie lub odejmowanie pomiędzy liczbami. W takim przypadku warto przygotować klasę statyczną:
static class OperacjeMatematyczne
{
    static int DodajLiczba(int a, int b)
    {
        return a + b;
    }
    static int OdejmijLiczby(int a, int b)
    {
        return a - b;
    }
}