1 C# Podstawy


Zmienne, Logika, Warunkowe 1
Switch, Warunek skrótowo, Break Continue 2
Tablice, Pętle Foreach, Tablice wielowymiarowe 3
Programowanie Obiektowe 4
Konstruktor 5
Tworzenie obiektów do tablicy 6
Właściwości 7
Przypadki metod (klas) 9
Przykłady konstruktorów 10
Konstruktor kopiujący 11
Klasy Zagnieżdżone 12
Dziedziczenie 13
Polimorfizm ,operatory IS AS  14
Virtual i Override, Klasy i metody abstrakcyjne 15
Interfejsy 16
Klasy oraz metody zapieczętowane, Typ ENUM eration  (wyliczanie) 18
Przestrzeń nazw 19
Struktury 20
Indeksery 21
Klasy typy generyczne (uogólnione) 22
Tworzenie swojego typu generycznego 23
Metody generyczne 24
Przeciążenie operatorów oraz konstruktor kopiujący 26
Własne operatory rzutowania 28
Wartość null dla typów wartościowych, Metody rozszerzające  29 
Wyjątki i ich obsługa  31

plik.doc

Zmienne

LICZBY CAŁKOWITE - INTEGER
             * sbyte (signedbyte) - -128 do 127, na 8 bit, 1 bajcie
             * short - -32.768 do 32.767, na 16 bit, 2 bajtach
             * int - ~-2mld do +2 mld, na 32 bit, 4 bajtach
             * long - -2^63 do 2^63-1, na 64 bit, 8 bajtów
             *
             * byte - 0 do 255, na 8 bitach
             * ushort (u - unsigned) - 0 do 65.535
             * uint - 0 do 2^32-1
             * ulong - 0 do 2^64-1 
             *
LICZBY ZMIENNOPRZECINKOWE - FLOATING-POINT Types
             *
             * float 7 znaków po przecinku - 1,5 * 10 ^-45 do 3.4 * 10 ^38 - 32 bitach
             * double od 15 do 16 - 5 * 10 ^ -324 do 1.7 * 10 ^ 308
             *
             * decimal 28 znaków po przecinku od 1 * 10 ^ - 28 do 7.9 * 10 ^ 28 - 128 bitach
             *
BOOLEAN - prawda lub fałsz
             *
             * true lub false
             *
TYP ZNAKOWY
             *
             * char
             *

TYP CIĄGU ZNAKÓW - STRING

przyklady:
bool czyOtrzymalReszte = false;
            Console.WriteLine(czyOtrzymalReszte);

            char znak = 'a';
            Console.WriteLine(znak);

            string imie = "Arkadiusz";
            Console.WriteLine(imie);

            Console.WriteLine("liczba = " + liczba);
            Console.ReadLine();

Logika


KONIUNKCJA - (i) - &&

ALTERNATYWA - (lub) - ||

ZAPRZECZENIE - negacja  * !


Logika bitowa

            * 0 - false

             * 1 - true

/* KONIUNKCJI - &

/* ALTERNATYWA - |

 /* ALTERNATYWA WYKLUCZAJĄCĄ (XOR) - ^ (albo)  tylko 1 0  albo   0 1  daje 1

* ~ - negacja


Warunkowe

 if (warunek)
             *  instrukcja;
  else if (warunek2)
             *  instrukcja2;
  else
             *  instrukcja3;


Switch

static void Main(string[] args)
        {
            string nazwaKraju = "US";
           
            switch (nazwaKraju)
            {
                case "PL":
                    Console.WriteLine("Jesteś z Polski :)");
                    break;
                case "EN":
                case "US":   
                case "UK":
                    Console.WriteLine("You speak English");
                    break;
                    
                default:
                    Console.WriteLine("Nie wiem po jakiemu mówisz ;)");
                    break;
            }
            Console.ReadLine();  
        }



Warunek skrótowo 

(WARUNEK) ?  gdy warunek jest spełniony (true) : gdy warunek jest niespełniony (false);

 string tekst = (a > b) ? "a jest większe od b" : "a jest mniej. od b, bądź = b";


Pętla for i while

* for (int i = 0; i < 10; i++)
             *  INSTRUKCJE


 while (i < 3)
            {
                Console.WriteLine(i);
                i++;
            }
            */
            do
            {
                Console.WriteLine(i);
                i++;
            } while (i < 3);



Break Continue

if (i == 3)
                    continue; //to co jest po CONTINUE nie jest wykonywane, natomiast pętla mimo wszystko jest KONTYNUOWANA
             
             
                if (i == 3)

                    break; //to co jest po break; NIE JEST WYKONYWANE, z najbliższej PĘTLI wychodzimy


Tablice 

int[] tab=new int[4];

Stworzoną mamy tablicę 10 elementową a przypisaliśmy 5 wartości

int[] tab;

            tab = new int[10];

            tab[0] = 5;
            tab[1] = 7;
            tab[2] = 4;
            tab[3] = 10;
            tab[4] = 20;

            for (int i = 0; i < tab.Length; i++)
                Console.WriteLine(tab[i]);

wyświetli jednak 10 wartości, ostatnie 5 będzie miało wartość 0.


Można przypisać wartości podczas tworzenia ( 2 sposoby)

int[] ciag = new int [] {1, 2, 3, 4, 5, 6 };
int[] ciag2 =  { 1, 2, 3, 4, 5, 6 };


Pętle Foreach


int[] tab = new int[] {1, 2, 3, 4, 5, 11, 12, 13, 14, 15};

            foreach (int val in tab)
            {
                if (val == 3)
                    Console.WriteLine("Wartość jest równa 3");
                else
                    Console.WriteLine(val);
            }

jeśli nie wiemy jakiego typu zmienne w tablicy zamiast INT val można VAR val



Tablice wielowymiarowe

int[ , ]  zm  =  new int[4,3];

 tworze sobie 4 pojemniki ktore moga przechowywac po 3 pojemniki

  pojemniki :         zmienne
      zm[0] :      zm[0,0]  zm[0,1]  zm[0,2]
      zm[1] :      zm[1,0]  zm[1,1]  zm[1,2]
      zm[2] :      zm[2,0]  zm[2,1]  zm[2,2]
      zm[3] :      zm[3,0]  zm[3,1]  zm[3,2]

 ROZMIAR 4*3 = 12

3D:
int[,,] d3 = new int[2,3,2]

Przykład:


string[,] tab2D = new string[4, 3];

for (int i = 0; i < tab2D.GetLength(0); i++)
        for (int j = 0; j < tab2D.GetLength(1); j++)
        {
         tab2D[i, j] = "tab2D [ " + i + ", " + j + " ] = " ;
        }

tab.GetLength(0) - pobiera długość pierwszego wymiaru (1) drugiego wymiaru.

Tablica o różnej długości

int[][] jaggedArray = new int[3][];

            jaggedArray[0] = new int[] { 5, 6, 7, 8 };
            jaggedArray[1] = new int[] { 5, 4 };
            jaggedArray[2] = new int[] { 4, 2, 1, 4, 5 };


Ponieważ długości drugiego wymiaru są różne, przy pobieraniu długości 2 wymiaru musimy odwołać się do  tab[i].Length   (i - który element pierwszego wymiaru)

int[][] jaggedArrayInLine = new int[][]
                                        {
                                            new int[] {1, 2, 3, 4, 5},
                                            new int[] {2, 1},
                                            new int[] {5, 7, 8}
                                        };

            for (int i = 0; i < jaggedArrayInLine.GetLength(0); i++)
            {
                for (int j = 0; j < jaggedArrayInLine[i].Length; j++)
                {
                    Console.Write(jaggedArrayInLine[i][j] + " ");
                }

                Console.WriteLine();
            }



Programowanie Obiektowe 

Przykład klasy

class Czlowiek
{
    string name;

    public string getName()  //publiczna,zwraca string
    {
      return name;
    }
    public void setName(string name) //publiczny, nic nie zwraca void,pobiera string
    {
        this.name = name;
    }
}


Stworzenie obiektu i wykorzystanie metod

        Czlowiek a = new Czlowiek();
        Czlowiek b = new Czlowiek();

        a.setName("Arkadiusz");
        b.setName("Asia");
  
        Console.WriteLine(a.getName());
        Console.WriteLine(b.getName());
 
        Console.ReadLine();  


Konstruktor

Nazwa taka sama jak klasy, publiczny, żeby był dostępny, nic nie zwraca ale bez void

class Gracz
{
    public Gracz()    //konstruktor
    {
    }
}
tworząc klasę Gracz a=new Gracz();           czyli Gracz()  wywołuje konstruktor

Przeciążony:

class Gracz
{
    public Gracz()    //konstruktor bezparametrowy
    {
        nick = null;
        id = 0;
    }
    public Gracz(string nick) //konstruktor z 1 parametrem
    {
        this.nick = nick;
        nastepnyId++;
        id = nastepnyId;
    }
    string nick;
    int id;
    static int nastepnyId = 0;   //zmienna statyczna (istnieje 1 i od razu nawet bez obiektu)
}


Statyczna może być też metoda w klasie, możemy wtedy ją wywołać nawet bez obiektu

 class Matematyka
{
    public static int dodaj(int a, int b)
    {
        return a + b;
    }
}
i wywołujemy nawet jak nie istnieje żaden obiekt:
Console.WriteLine(Matematyka.dodaj(5,7));



Tworzenie obiektów do tablicy

 Gracz[] gracz = new Gracz[3];

        gracz[0] = new Gracz("Morfidon");
        gracz[1] = new Gracz("Jakiś nick 1");
        gracz[2] = new Gracz("trallala");


Przykład z właściwościami. Właściwości opisane poniżej przykładu

 class Gracz
{
    public Gracz()   //konstruktor bezparametrowy
    {
        nick = null;
        id = 0;
    }
    public Gracz(string nick)  //konstruktor z 1 parametrem
    {
        this.nick = nick;
        nastepnyId++;
        id = nastepnyId;
    }
    string nick;         //dane,pola,parametry 
    int id;
    static int nastepnyId = 0;   // zmienna statyczna

    public string Nick   //wlasciwosc (get/set)
    {
        get
        {
            return this.nick;
        }
        set
        {
            this.nick = value;
        }
    }
    public int Id  //wlasciwosc tylko get (nie zezwala na ustawienie)
    {
        get
        {
            return this.id;
        }
    }

    public static int NastepnyId   //wlasciwosc tylko get (tylko pozwala na pobranie)
    {
        get
        {
            return nastepnyId;
        }
    }
}



Właściwości 

Pola(dane) w klasach .NET są zawsze prywatne natomiast właściwości udostępniają nam zmienne.

Przykład wykorzystania pól:
struct Punkt3D
{
    public double GetX()
    {
        return this.X;
    }

    public void SetX( double newX)
    {
        this.X = newX;
    }


    public Punkt3D(double x)   //konstruktor 
    {
        X = x;
    }

    private double X;  / pole (dana)
}

i w kodzie wykorzystanie:

Punkt3D p3 = new Punkt3D(43);

double zpos = p3.GetZ();
p3.SetZ(zpos + 30);
.

Właściwości to mechanizm łączący zachowania pól i metod w C#.

Właściwości wyglądają jak pola więc możemy wyświetlić ich wartość bądź je przypisać ,a zarazem zachowują się jak metody czyli możemy stosować je w interfejsach.

W Intellisense Visual Studio właściwości mają ikonkę listy z rączką. Możesz z opisu każdej właściwości wyczytać czy są one tylko do odczytu (GET) , czy też można je ustawić (SET). Pola tego nie potrafią.
Pola mają ikonkę niebieskiego sześcianu.

wszystkie klasy w .NET mają pola prywatne czyli raczej tej ikonki nie zobaczysz wewnątrz jakieś struktury, czy klasy.

Deklarowanie:

ModyfikatorDostępu [Typ] NazwaWłaściwości
{
    get
    {
        //Kod, który odczytuje właściwość       
    }

    set
    {
        //Kod, który ją zwraca       
    }



Zmieńmy kod struktury z Punktu3D tak, aby korzystała ona z właściwości.

Visual Studio ułatwia nam życie po raz kolejny . Korzystając z opcji “Encapsulate Field ” Visual Studio może wygenerować dla nas właściwość dla danego pola prywatnego trzeba go tylko zaznaczyć.




Przykład struktury Punkt3D z właściwościami:

struct Punkt3D 
{
    private double x;

 

    public double X
    {
        get { return x; }
        set { x = value; }
    }}


właściwości zaczynają się z dużej litery ,a pola z małej. Jest to ogólnie przyjęta konwencja nazw.

Jak widzisz w bloku kodzie SET zostało użyte słowo kluczowe value. Słowo referuje się do wartości przekazanej przez nas do właściwości.

Przykład właściwości  Czas  pracy:

struct CzasPracy
{
    private int sekundy;

    public int Sekundy
    {
        get { return sekundy; }
        set { sekundy = CheckTime(value); }
    }

    private int minuty;

    public int Minuty
    {
        get { return minuty; }
        set { minuty = CheckTime(value); }
    }
    //Statyczna prywatna metoda sprawdzająca poprawność danych 
    private static int CheckTime(int czas)
    {
        return czas % 60;
    }

    public CzasPracy(int s,int m)
    {
        sekundy = CheckTime(s);
        minuty = CheckTime(m);
    }
}


Przykład działania, wykorzystanie w kodzie:

CzasPracy czas = new CzasPracy(10, 0);

int m = czas.Minuty; //Blok GET dla Minuty 
int s = czas.Sekundy; //Blok GET dla Sekundy 

czas.Minuty = m; //Blok SET dla Minuty 
czas.Sekundy = s; //Blok SET dla Sekundy 
czas.Sekundy -= 10; //Blok GET i SET dla Sekundy 
czas.Minuty += 10;  //Blok GET i SET dla Sekundy
.


Właściwość bez słowa kluczowego SET będzie tylko do odczytu.

W podobny sposób możesz stworzyć właściwość tylko do zapisu.

UWAGI:
-Właściwości muszą posiadać chociaż jedno słowo kluczowe GET lub SET.
-GET i SET nie mogą pobierać żadnych parametrów.
-Właściwość składa się tylko z dwóch bloków kodów GET i SET żadnych dodatkowych pól,metod i kolejnych właściwości.
-Właściwość może być przypisana tylko gdy struktura i klasa jest zainicjowana. Czyli teoria o możliwość użycia struktur bez słowa kluczowego new  może pójść do kosza. Skoro pola muszą być prywatne ,a właściwości nie mogą być użyte bez inicjacji.
-Właściwości to nie pola więc nie mogą być używane ze słowami kluczowymi REF i OUT. Czyli jeśli metoda potrzebuje zmiennej z REF i OUT właściwość bezpośrednio nie może być użyta. Ma to całkowity sens logiczny w końcu właściwości nie odnoszą się do specyficznego miejsca w pamięci. 
-Właściwość nie mogą być stałe ani const.

interfejsy i wlasciwosci oraz wirtualne wlasciwosci na:
https://cezarywalenciuk.pl/blog/programing/post/kurs-obiektowosc-w-c-wasciwosci-15


Przypadki metod (klas)

statyczne, czyli możemy używać bez tworzenia obiektów

 class Math
{
 
    public static int Abs(int x)
    {
        
        if (x < 0)
            return x * (-1);
        
        return x;
    }

    public static int IncreaseBy5(int nr)
    {
        nr += 5;
        return nr;
    }
    public static int IncreaseBy5(ref int nr)  //przez referencje, zmienia orginal
    {
        nr += 5;
        return nr;
    }
    public static void Zeruj(out int b) //podobna do ref ale out musi byz zainicjowana wartoscia tu, nie wczesniej.
    {
        b = 0;
    }
    public static int Dodaj(params int[] args) //params -rozna ilosc argumentow,dostep jak przez tablice, ale NIE
    {
        int suma = 0;                          //podajemy jako parametrow tablicy, ale inty  2,4,8,6,5...

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

        return suma;
    }
    public static int Poteguj(int podstawa_potegi, int wykladnik = 2)
    {
        // 2 ^ 3 = 2 * 2 * 2 = 8
        int tmp = 1;
        
        for (int i = 0; i < wykladnik; i++)
        {
            tmp *= podstawa_potegi;
        }

        return tmp;
    }
}



Przykłady konstruktorów

zmienna now typu DateTime ma dostęp do metody Hour
DataTime now = DiteTime.Now;    //tworzenie

now.Hour          //uzycie Hour i mamy dostęp do aktualnej godziny


Konstruktor uruchamiany tylko podczas tworzenia obiektu:

 class Program
{
    static void Main(string[] args)
    {
        Welcomer aa = new Welcomer();
        Console.WriteLine(aa.Message);
        Console.WriteLine(aa.Message);
        Console.WriteLine(aa.Message);
        Console.WriteLine(aa.Message);
        Console.ReadLine();
    }
}
class Welcomer
{
    public string Message // publiczna, wlasciwosc typu string 
    {
        get;
        private set;
    }

    public Welcomer()   //konstruktor, tylko podczas tworzenia obiektu 
    {
        Console.WriteLine("lala");
        DateTime now = DateTime.Now;

        if (now.Hour < 17)
            this.Message = "Dzień dobry";
        else
            this.Message = "Dobry wieczór";
    }
}

dlatego wyświetli

lala
Dzień dobry
Dzień dobry
Dzień dobry
Dzień dobry

Jeśli właściwość damy statyczną i konstruktor statyczny możemy wywołać właściwość Message bez tworzenia obiektu odwołując się do Klasy a nie obiektu

        Console.WriteLine(Welcomer.Message);
        Console.WriteLine(Welcomer.Message);
        Console.WriteLine(Welcomer.Message);
        Console.WriteLine(Welcomer.Message);

i wtedy również konstruktor zostanie wywołany tylko raz, na początku.


Kolejny przykład


      class Car
{
    public string Description  //wlasciwosc opis
    {
        get;
        set;
    }
    public byte NrOfWheels   // wlasciwosc ilosc kół
    {
        get;
        private set;
    }

    public Car(string description, byte nrOfWheels) //konstruktor 2 parametrowy
    {
        this.Description = description;
        this.NrOfWheels = nrOfWheels;
        Console.WriteLine("dupa");
    }
    public Car(string description) : this(description, 4) //kons.1 parametrowy który
    {     // najpierw wywołuje wywołuje konstruktor dwu parametrowy :this(xxx,yyy)
        Console.WriteLine("lala");
    }
    public Car(Car samochod)  //konstruktor kopiujący
    {
        this.Description = samochod.Description;
        this.NrOfWheels = samochod.NrOfWheels;        
    }
}

Jeśli stworzymy obiekt podając 1 parametr

Car samochod = new Car("Jakiś opis");

zostanie najpierw wywołany konstruktor 2 parametrowy ( drugi parametr = 4) i wypisze "dupa"
i dopiero wtedy konstruktor 1 parametrowy i wypisze "lala"

Tworząc obiekt i podając 2 parametry nie wypisze "lala"



Konstruktor kopiujący:

Tworząc obiekt i podając jako parametr istniejący obiekt

Car samochod2 = new Car(samochod);

skorzysta z konstruktora kopiującego.

Uwaga BEZ konstruktora kopiującego nie stworzy nowego obiektu bo zaspi  "new Car(obiekt);" nie zadziała.
Stosując natomiast taki zapis:

Car samochod2 = samochod;

stworzymy tylko kolejne odwołanie do tego samego obiektu!!!




Klasy Zagnieżdżone

Możemy wewnątrz klasy stworzyć inna zagnieżdżoną klasę, będzie ona działała na potrzeby głównej klasy. W przykładzie tworzymy prywatną klasę Silnik i prywatną zmienną Silnik (musi być taka sama)
W Konstruktorze samochodu tworzymy obiekt silnika. Wewnątrz klasy mamy dostęp do ob. silnika.


      class Samochod
{
    private class Silnik   //klasa zagnieżdżona
    {
        public Silnik(uint moc = 500)  //konstruktor
        {
            this.Moc = moc;
            Console.WriteLine("ja jestem z konstruktora silnik");
        }
        public uint Moc;   //dana,pole
    }

    public Samochod()  //konstruktor
    {
        silnik = new Silnik();  //tworzenie obiektu Silnik
        silnik.Moc = 700;       //zmiana wartosci pola Moc silnika 
        Console.WriteLine("konstruktor samochodu");
    }
    public uint pobierzMoc()
    {
        return this.silnik.Moc;
    }
    private Silnik silnik;  //tworzenie zmiennej klasy Silnik (nie obiekt)
}

Jeśli zamienimy modyfikator klasy Silnik i zmiennej silniki z private na public to możemy stworzyć
obiekt silnika poza klasą Samochód:
KlasaGłówna.KlasaZagnieżdżona  zmienna = new  KlasaGłówna.KlasaZagnieżdżona();

Przykład:

     Samochod samochod = new Samochod(); //tworzymy obiekt samochód 
        Console.WriteLine(samochod.pobierzMoc()); //wypisujemy jego moc za pomocą metody
        
        
        Samochod.Silnik silnik = new Samochod.Silnik();  //tworzymy SAMODZIELNY silnik
        silnik.Moc = 500;
        Console.WriteLine("samodzielny " + silnik.Moc);
        
        Console.ReadLine();



Dziedziczenie

klasa bazowa
     class Punkt
{
    public int X
    {
        get;
        protected set;  //protected żeby kl.dziedzicząca mogła mieć dostęp,reszta prv
    }
    public int Y
    {
        get;
        protected set;  //protected żeby kl.dziedzicząca mogła mieć dostęp,reszta prv
    }
    public Punkt()  //konstruktor domyślny
    {
        Console.WriteLine("jesteś konstruktorem domyślnym z klasy Punkt");
        this.X = 0;
        this.Y = 0;
    }
    public Punkt(int X, int Y) //konstruktor 2 parametrowy 
    {
        Console.WriteLine("jesteś konstruktorem Punkt z 2 parametrami z klasy Punkt");
        this.X = X;
        this.Y = Y;
    }
    public string pobierzWsp()  //metoda zwraca x i y jako string 
    {
        return this.X + ", " + this.Y;
    }
};


Klasa bazowa ma protected co umożliwia dostęp klasie pochodnej dla reszty protected.
Klasa pochodna, dziedziczy po klasie bazowej Punkt         : Punkt
Jej konstruktor żeby nie przepisywać zachowania z X i Y bazuje na konstruktorze bazowym base(X,Y)
i dopisuje trzeci parametr Z

Metoda pobierzWsp jest nadpisana, może być bez new, ale wtedy kompilator ostrzega że nadpisaliśmy, new informuje, że zrobiliśmy to świadomie.
W metodzie korzystamy z metody bazowej  base.pobierzWsp()  i dodajemy do niej treść.


    class Punkt3d : Punkt
{
    public int Z
    {
        get;
        protected set;
    }
    public Punkt3d(int X, int Y, int Z) : base(X, Y) //konstruktor bazujący na bazowym 2 elem.
    {
        this.Z = Z;
    }
    new public string pobierzWsp()  //dopisuje do istniejącej metody klasy basowej
    {                               //new dodany żeby potwierdzić nadpisanie, wiem co robie
        return base.pobierzWsp() + ", " + this.Z; //koszyta  z met.bazowej + nowa tresc
    }
}




Polimorfizm ,operatory IS  AS

Tworzymy dwie zmienne jednego typu.

Punkt a;
Punkt b;
a=new Punkt(1,5);
b=new Punkt3D(1,3,7);

Jest to możliwe bo klasa Punkt3D dziedziczy po Punkt. Po co? By mieć zmienne jednego typu
co umożliwia np zapisywanie w tablicy konkretnego typu, albo przekazywanie jako parametr do funkcji konkretnego JEDNEGO typu.

Jakkolwiek czyste wywołanie metody b.pobierzWsp()  uruchomi metodę tylko z klasy bazowej (czyli bez dopisanego wyświetlania punktu Z) to rzutowanie w górę.
Żeby dostać jednak dostęp do metody z klasy pochodnej musimy rzutować w dół:

((Punkt3d)b).pobierzWsp()


Polimorfizm nie działa w odwrotną stronę czyli zmienna klasy pochodnej a przypisany obiekt bazowy.

Punkt3d c=new Punkt( 5,10 );     / ZLE



Operator IS i AS

Co wtedy gdy nie wiemy czy dostaniemy obiekt typu Punkt (który ma 2 zmienne i nie trzeba rzutować) czy obiekt typu Punkt3D i musimy rzutować żeby wyświetlić 2 parametr?

Możemy to sprawdzić

za pomocą IS wygląda to tak:

 static void Display(Punkt p)
    {
        if (p is Punkt3d)
            Console.WriteLine(((Punkt3d)p).pobierzWsp());
        else
            Console.WriteLine(p.pobierzWsp());
    }



za pomocą AS tak:


  static void Display(Punkt p)
    {
        
        //Punkt3d p3d = ((Punkt3d)p); //tak sie dzieje, gdy mamy przypadek, 
        //że p jest typu Punkt3d
        
        Punkt3d p3d = p as Punkt3d; //wiec kiedy nie jest możliwe to w p3d będzie NULL;

        if (p3d == null)
            Console.WriteLine(p.pobierzWsp());
        else
            Console.WriteLine(p3d.pobierzWsp());
    }


Każda klasa i tak dziedziczy po klasie Object


static string getClassName(Object x)
    {
       x.  //wyswietla dostepne metody klasy Obiekt
        return x.ToString();
    }

więc wypisując x.   podpowiada nam jakie mamy dostępne metody odziedziczone po klasie Object
tu korzystamy z metody ToString która zwróci nazwę obiektu.

co ciekawe jak napiszemy:

if (x is Punkt)
            ((Punkt)x).        - to zacznie nam pokazywać dostepne metody klasy Punkt!




Virtual i Override


Przez to że metoda pobierzWsp klasy Punkt jest nadpisana tak samo nazwaną metodą w klasie dziedziczącej Punkt3D to musimy przy odwoływaniu się do obiektu 3D rzutować (powyżej)

chyba, że w klasie bazowej metodę tą poprzedzimy Virtual a w klasie pochodnej override
wtedy zawsze będzie wywoływał metodę klasy pochodnej (w przypadku gdy obiekt jest klasy pochodnej, ale TYPU bazowej)


Punkt a;
Punkt b;
b=new Punkt3D(1,3,7);    //obiekt Punkt3D typu Punkt, klasa 3D koniecznie pochodna Punkt




Klasy i metody abstrakcyjne


Jeśli klasa jest abstrakcyjna, to nie można stworzyć z niej obiektu. Służy do dziedziczenia.
Jeśli metoda w klasie jest abstrakcyjna, to w klasie pochodnej będzie musiała być opisana. Inaczej kompilator zwróci błąd. Czyli stworzenie metody abstrakcyjnej sprawia, że będziemy musieli ją napisać w klasie pochodnej.

Czyli jeśli metoda jest public to możemy ją nadpisać za pomocą new, jeśli virtual to zawsze zostanie wybrana metoda z klasy pochodnej jeśli jest nadpisana za pomoca override. A jeśli jest abstract to będzie musiała być koniecznie nadpisana override.

Do właściwości klasy w obiekcie klasy pochodnej (ale typu klasy bazowej)  możemy mieć dostęp tylko przez rzutowanie (nie ma właściwości virtual). Dlatego lepiej jest zrobić sobie metodę abstrakcyjną, która daje nam dostęp do tej właściwości, opisać ją w klasie pochodnej i możemy ją wywoływać bez rzutowania.


   Osoba[] osoby = new Osoba[4];   //tworzymy tablicę zmiennych typu Osoba, bez obiektów

        osoby[0] = new Pracownik("Arkadiusz", "Włodarczyk", 300000); //Pracownik kl pochodna
        osoby[1] = new Student("Jan", "Kowalski", 1);    //Student kl pochodna Osoby
        osoby[2] = null;            
        osoby[3] = null;
        
        for (int i = 0; i < osoby.Length; i++)    //dlugość jest 4 ale obiekty tylko w 2
        {
            if (osoby[i] != null)         //dlatego sprawdzamy, czy istnieje obiekt
                Console.WriteLine(osoby[i].Opis());  //dzieki temu, ze Opis jest abstrakcyjny
        }                       //nie musimy rzutować bez względu czy o. Pracownik czy Student




Powyższy warunek if możemy zapisać jako metodą statyczną (istniejąca bez obiektu) naszej klasy abstrakcyjnej Osoba, metodą przyjmującą jako parametr tablicę obiektów typu Osoba.


  public static void wypiszElementy(Osoba[] osoby)
    {
        for (int i = 0; i < osoby.Length; i++)
        {
            if (osoby[i] != null)
                Console.WriteLine(osoby[i].Opis());
        }
    }



i wywołać to tak:

Osoba.wypiszElementy(osoby);

efekt ten sam.



Interfejsy 


 interface IEkran
{
    void Wyswietl(); //tu public domyślnie i troche jak abstract
}

class Monitor : IEkran //implementacja interfejsu w klasie, nie dziedziczenie
{
    public void Wyswietl() //musi być public,jak abstract, ale ovveride nie musi byc
    {

    }
}


W interfejsie nie może być pól (zmiennych), mogą być tylko właściwości
W interfejsie wszystkie metody są abstrakcyjne (wymagaja opisania w klasie implementacji)
W interfejsie nie można tworzyć konstruktorów.
Klasa może implementować kilka interfejsów.

Po co? Gdy chcemy stworzyć wspólny sposób dostępu do różnych klas. Np różne banki implementują ten sam interfejs, czyli każdy ma to samo nazewnictwo metod (co pozwoli na komunikację za pomocą polimorfizmu)

Co ciekawe, kiedy potem tworzymy zmienne obiektów dla banków to będą obe typu...interfejs.
Tak jak z klasą bazową i pochodną.

Przykład interfejsu IKontoBankowe i Klasy  KontoA


interface IKontoBankowe
{
    decimal Saldo
    {
        get;        
    }
    void Wplac(decimal ilosc);
    bool Wyplac(decimal ilosc);
    bool PrzetransferujDo(IKontoBankowe cel, decimal ilosc);
}
class KontoA : IKontoBankowe
{
    private decimal saldo;
    public decimal Saldo
    {
        get
        {
            return saldo;
        }
    }
    public void Wplac(decimal ilosc)
    {
        saldo += ilosc; // saldo = saldo + ilosc;
    }
    public bool Wyplac(decimal ilosc)
    {
        if (saldo > ilosc)
        {
            saldo -= ilosc;
            return true;
        }

        Console.WriteLine("Za mało funduszy na koncie");
        return false;
    }
    public bool PrzetransferujDo(IKontoBankowe cel, decimal ilosc)
    {
        bool wyplacono = this.Wyplac(ilosc);

        if (wyplacono)
            cel.Wplac(ilosc);

        return wyplacono;
    }
}


Klasa innego banku, KontoB wygląda tak samo, ma tylko inna nazwę.

Przykład tworzenia obiektów:


        IKontoBankowe a = new KontoA();
        IKontoBankowe b = new KontoB();

        a.Wplac(500);
        b.Wplac(500);
        a.PrzetransferujDo(b, 100);

        Console.WriteLine(a.Saldo);
        Console.WriteLine(b.Saldo);

Jak opisane wyżej, są one typu interfejs co pozwala na polimorfizm (wspólny typ dla różnych obiektów KontoA i KontoB)


Warto zwrócić uwagę na metodę PrzetransferujDo  które potrafi wypłacić ze swojego konta (z obiektu który wywołuje) do obiektu który podajemy jako parametr) Zastosowanie this i zmiennej parametru CEL


        public bool PrzetransferujDo(IKontoBankowe cel, decimal ilosc)
    {
        bool wyplacono = this.Wyplac(ilosc);

        if (wyplacono)
            cel.Wplac(ilosc);

        return wyplacono;
    }


wywołanie

a.PrzetransferujDo(b, 100);




Klasy oraz metody zapieczętowane

Klasę w przestrzeni globalnej programu nie można dać private. Tylko klasa w klasie może być private.
Jeśli chcemy zabezpieczyć klasę korzystamy ze słówka SEALED. Nie można wtedy dziedziczyć z takiej klasy.

Metodę z klasy bazowej możemy zabezpieczyć po prostu za pomocą provate, nie potrzebujemy sealed.
Ale jeśli mamy publiczną, którą klasa pochodna odziedziczy i nadpisze, nie możemy jej nagle zmienić w klasie pochodnej na prywatną. Musi pozostać publiczna  (override public). Wtedy jeśli chcemy zabezpieczyć przed dalszym jej dziedziczeniem dodajemy sealed



Typ ENUM eration  (wyliczanie)

tworzymy nowy typ, ale nie kończymy średnikiem  enum nazwatypu;
tylko:

public enum TimeOfDay
    {
        Morning, Afternoon, Evening
    }

gdzie automatycznie pierwsza Morning=0 druga Afternoon=1 itd...

teraz tworząc zmienna tego typu

TimeOfDay a =TimeOfDay.

po kropce kompilator sam będzie nam podpowiadał dostępne elementy Morning,Afternoon...


możemy stworzyć metodę która jako argument przyjmuje zmienna tego typu


public static string getGreetings(TimeOfDay time)
    {
        switch (time)
        {
            case TimeOfDay.Morning:
                return "Good Morning";
            case TimeOfDay.Afternoon:
                return "Good Afternoon";
            case TimeOfDay.Evening:
                return "Good Evening";
            default:
                return "Something went wrong";
        }
    }

(nie ma break bo daje return czyli wychodzi z funkcji)

stworzenie zmiennej na 2 sposoby

TimeOfDay a = TimeOfDay.Morning;

ale jeśli chcemy napisać cyfrą

TimeOfDay a = (TimeOfDay)1;

to wpisanie samej jedynki nie zadziała, musimy rzutować typ i wtedy przyjmie

uruchomienie metody:
Console.WriteLine(getGreetings(a));



a jeśli wyświetlimy samą zmienna a

Console.WriteLine(a);

wyświetli: Afternoon, nie 1



Podczas tworzenia typu można przypisać inne wartości, ale wtedy dalsze elementy przypisuje kolejno od ostatniej wartości

Morning=0, Afternoon=4, Evening

czyli Evening będzie 5  a brakować będzie 1,2,3



Przestrzeń nazw


Jeśli mamy klasę i główny program w jakiejś przestrzeni nazw a jakąś klasę bez przestrzeni nazw i chcemy się do niej odwołać to używamy

global::nazwa

JEśli jest przestrzeń nazw w innej przestrzeni nazw to żeby się odwołać wypisujemy po kolei
jedna.druga.trzecia.funkcja();

Można dodać przestrzeń do using, ale wtedy identycznie nazwane funkcje w różnych przestrzeniach będą kłopotliwe do wywołania.
Można stworzyć alias

 using skrot = JakisInny.tralla.hmm;

wywołując można odwoływać się przez skrót.funkcja

ale lepiej przez ::  czyli skrót::funkcja, bo jesli stworzymy klasę skrót będzie problem



Struktury

Podobnie do klas, ale nie ma dziedziczenia. Można implementować interfejsy. Nie można stworzyć konstruktora bezparametrowego.
Struktury powstają na stosie(stack) nie na stercie (hip)

przykład:


struct KsiazkaAdresowa
{
    public String Imie;
    public String Nazwisko;
    public String Telefon;
    public KsiazkaAdresowa(string Imie, string Nazwisko, string Telefon)
    {
        this.Imie = Imie;
        this.Nazwisko = Nazwisko;
        this.Telefon = Telefon;
    }
}


do stworzenia wystarczy (tworzy na stosie):

KsiazkaAdresowa ka;

Jeśli mamy klasę np KsiazkaAdresowa2 to żeby stworzyć trzeba użyć new (tworzy na stercie):

KsiazkaAdresowa2 ka2 = new KsiazkaAdresowa2();


Jeśli jednak podczas tworzenia zmiennej struktury chcemy przypisać jej wartości to używamy new.
Nie musi on rezerwować pamięci, bo samo stworzenie zmiennej już to robi (w przeciwieństwie do klasy) ale pozwala na uruchomienie konstruktora z parametrami


KsiazkaAdresowa h = new KsiazkaAdresowa("Arkadiusz", "Zawadzki", "999 111 222");




Jeśli chcemy stworzyć tablicę struktur też używamy new:


KsiazkaAdresowa[] a = new KsiazkaAdresowa[40];


i od razu możemy się odwoływać:

 a[0].Imie = "Arkadiusz";


Jeśli w taki sam sposób stworzymy tablicę zmiennych typu klasa:

KsiazkaAdresowa2[] b = new KsiazkaAdresowa2[40];


to jeszcze musimy w każdym polu stworzyć obiekt za pomocą new żeby się odwoływać:


for (int i = 0; i < b.Length; i++)
     b[i] = new KsiazkaAdresowa2();



Różnica pomiędzy strukturą i klasą będzie także podczas przekazywania ich jako argument do funkcji. Struktura przekazuje przez wartość (tworzona jest kopia) a obiekt przez referencję (zmiana wpływa na oryginalny obiekt)


public static void test(KsiazkaAdresowa t)
    {
        t.Imie = "test";
    }

public static void test(KsiazkaAdresowa2 t)
    {
        t.Imie = "test";
    }


i teraz jak wywołamy


        test(ka);
        test(ka2);

        Console.WriteLine(ka.Imie);
        Console.WriteLine(ka2.Imie);


to zapis struktury ka nie zmieni się a obiekt k2 będzie miał w polu Imię zapis "test".


żeby struktura została zmieniona trzeba by wymusić przekazanie przez referencję
funkcja:          public static void test(ref KsiazkaAdresowa t) {}
wywołanie:     test(ref  ka);




Indeksery

do zwykłego stringu możemy się odwoływać poprzez indeks pozycji

string a = "to jest zdanie testowe";

Console.WriteLine(a[1]);

Zachowuje się tak, ponieważ traktowany jest jak klasa.

Stwórzmy więc klasę ale zachowującą się tak,że każde odwołanie przez [nr] dotyczy słowa nie litery. Możemy to osiągnąć przez indekser


class Zdanie
{
    public string[] slowa;
    public Zdanie(string zdanie)
    {
        this.slowa = zdanie.Split(); //split wrzuca każde słowo w kolejne pola tablicy
    }

    public string this[int n]   //indekser- this czyli obiekt [parametr] i co ma zwracac
    {
        get
        {
            if (slowa.Length > n)
                return slowa[n];
            else
                return null;
        }
        set
        {
            if (slowa.Length > n)
             slowa[n] = value;
        }
    }
}


i teraz możemy odwołując się do pozycji zarówno zapisać/podmienić całe słowo jak i wyświetlić pojedyncze słowo


Zdanie a = new Zdanie("to jest testowe zdanie");
        a[0] = "lalala";
        Console.WriteLine(a[0]);

ewentualnie wyświetlić cały string


Console.WriteLine(string.Join(" ",a.slowa));

albo tradycyjnie


foreach (var item in a.slowa)
        {
            Console.Write(item+" ");
        }





Klasy typy generyczne (uogólnione)

W uproszczeniu można powiedzieć, że typy generyczne są “szablonami”. Dzięki typom generycznym możemy uniknąć niepotrzebnego rzutowania. Klasy generyczne pozwalają na tworzenie obiektów tej samej klasy dla różnych typów danych. Dopiero przy tworzeniu obiektu klasy generycznej podajemy typ danych na którym będzie operował.

ArrayList   wymaga  using System.Collections;

Lepsza tablica, nie trzeba określać długości. Przyjmuje obiekt, więc można przypisać co chcemy i również to mieszać


  ArrayList lista = new ArrayList();

        lista.Add(5);
        lista.Add(10);
        lista.Add(15);
        lista.Add(20);
        lista.Add("ffgasdgsdgf");
        lista.Add(new Test("To jest wartosc z egzemplarza klasy Test"));

        for (int i = 0; i < lista.Count; i++)
        {
           // Console.WriteLine(((Test)lista[i]).testowyString);
            Console.WriteLine(lista[i].ToString());
        }


Jeśli w pętli wyświetlany teraz po kolei elementy tablici i trafi na obiekt (ostatni element) to wyświetli nam tylko nazwę klasy z jakie obiekt pochodzi. Nie mamy też dostępu (po kropce) do elementów obiektu (pól i metod). Żeby mieć taki dostęp musielibyśmy rzutować (zakomentowany przykład) ale wtedy będzie problem z elementami tablicy które nie są obiektem rzutowanej klasy.


Tworząc Listę typów generycznych określamy jakiego dokładnie typu się spodziewamy


List   wymaga    using System.Collections.Generic;


  List<int> listaIntegerow = new List<int>();

        listaIntegerow.Add(10);
        listaIntegerow.Add(20);

        List<string> listaStringow = new List<string>();
        
        listaStringow.Add("lala");
        listaStringow.Add("hahaha");

        List<Test> listaTestow = new List<Test>();

        listaTestow.Add(new Test("glsahaslkjdgh"));
        listaTestow.Add(new Test("jkfgjndfjkggf"));

i teraz w przypadku listy obiektów (ostatnia) możemy odwoływać się do elementów obiektu (pola, metody) bez rzutowania:


  for (int i = 0; i < listaTestow.Count; i++)
        {
           Console.WriteLine(listaTestow[i].testowyString);
        }


testowyString musi być oczywiście publiczny albo być właściwością



Tworzenie swojego typu generycznego

tworzymy jak klasę ale w <> nazwę typu, w srodku zmienna naszego typu (niewiadomego jeszcze)
i motedo które ją zwracają bądź ustawiają (ewentualnie właściwość jak zakomentowane)


  class Zwykla <COKOLWIEK>
{
    COKOLWIEK nazwaZmiennejTypuGenerycznego;
    public COKOLWIEK zwrocWartosc()
    {
        return this.nazwaZmiennejTypuGenerycznego;
    }
    public void nadajWartosc(COKOLWIEK nazwaZmiennejTypuGenerycznego)
    {
        this.nazwaZmiennejTypuGenerycznego = nazwaZmiennejTypuGenerycznego;
    }
    /*
    public COKOLWIEK nazwaZmiennejTypuGenerycznego
    {
        get;
        private set;
    }
     * */
}



i potem przy tworzeniu określamy równie w <  > jakiego konkretnego  typu ma być, tu String:


  var zm = new Zwykla<String>();

        zm.nadajWartosc("to jest tekst z klasy zwykla");
        Console.WriteLine(zm.zwrocWartosc());

var automatycznie rozpoznaje typ, tu będzie
Zwykla<String>   zm   = new Zwykla<String>();

i mamy zmienna którą określamy przy tworzeniu i która może mieć określone metody



Metody generyczne

Przykład


public static void Swap<T>(ref T a, ref T b)
    {
        T tmp;

        tmp = a;
        a = b;
        b = tmp;
    }


użycie


        int a = 10;
        int b = 20;

        string c = "lala";
        string d = "haha";

        Swap(ref a, ref b);
        Swap<string>(ref c, ref d);


Jak widać w pierwszym użyciu, nie trzeba nawet podawać typu, kompilator sam się domyślił.



Dziedziczenie typów generycznych, statyczne i domyślne wartości 

Przykład:
Klasa uogólniona (generyczna) czyli szablon do tworzenia klas z różnymi typami.
Tworzymy właściwość zm i bezparametrowy konstruktor (który nadpisuje domyślny).
Nie da się w nim przypisać konkretnej wartości np. 0 bo nie znamy typu. Dlatego przypisujemy default(t) i przy typie wartościowym przypisze 0 a przy referencyjnym null.


       class Test<T> 
{
    public T zm  //wlasciwosc typu T
    {
        get;
        private set;
    }

    public Test() //nadpisany konstruktor domyslny
    {
        this.zm = default(T); //nie możemy przypisac 0, bo nie znamy typu
    }
}


Jeśli chcemy ograniczyć jakie typy może przyjmować taka klasa to piszemy


      class Test<T> where T : class
{

}

inne typy CONSTRAINT (przymus)

where T : class - T  MUSI być typu klasowego (obiekt, ale i string)
 where T : struct - T musi być typu strukturalnego (np int jest tego typu)
 where T : INazwaInterfejsu - typ T musi implementować INazwaInterfejsu, lub też jest właśnie tym interfejsem
 where T : NazwaKlasy - typ T musi być typem NazwaKlasy lub też typem dziedziączym po NazwaKlasy
 where T : new() - wymaga aby typ T miał bezparametrowy konstruktor

można łączyć po przecinku np   where T : class,new()


Dziedziczenie w klasach generycznych


abstract class Kalkulator<T> where T : struct
{
    public abstract T Dodaj(T x, T y);
    public abstract T Odejmij(T x, T y);
}

i podczas dziedziczenia kiedy określamy typ klasy bazowej (generycznej) podczas wypełniania metod abstrakcyjnych w polu argumentów sam uzupełni typ


class IntKalkulator : Kalkulator<int>
{
    public override int Dodaj(int x, int y)
    {
        return x + y;
    }
    public override int Odejmij(int x, int y)
    {
        return x - y;
    }
}

klasa dziedzicząca też może być generyczna

class DoubleKalkulator<U> : Kalkulator<double>
{
    U zmienna;
...
}


Statyczne zmienne wewnątrz klas uogólnionych


lass StaticTest<T>
{
    public static int x;
}


i dla każdej wersji typu będzie OSOBNA zmienna statyczna


        StaticTest<int>.x++;

        StaticTest<double>.x++;



Przeciążenie operatorów oraz konstruktor kopiujący


Kiedy mamy klasę i chcemy porównać dwa obiekty tej klasy

        StanKonta a = new StanKonta(10, 5);
        StanKonta b = new StanKonta(10, 5);

        Console.WriteLine(a==b);

to pokaże false, bo to dwa różne obiekty

Żeby samemu określić co ma sprawdzać operator == trzeba go przeciążyć.
W klasie musimy dodać jednocześnie przeciążenie dla różnicy (!=) żeby zadziałało


    public static bool operator ==(StanKonta L, StanKonta P)
    {
        if (L.Gr == P.Gr && L.Zl == P.Zl)
            return true;

        return false;
    }
    public static bool operator !=(StanKonta L, StanKonta P)
    {

        return !(L==P);
    }


Zl i Gr to właściwości klasy.   L  i P określające zmienną lewą i zmienna prawą (to obiekt klasy)

wlasciwośc Zl

public uint Zl
    {
        get;
        private set;
    }



klasa z konstruktorami:


class StanKonta
{
    public StanKonta(uint zl, ushort gr) //konstruktor 2 parametrowy
    {
        this.Zl = zl;
        this.Gr = gr;
    }
    public StanKonta(StanKonta k)  //konstruktor kopiujacy z innego obiektu
    {
        this.Gr = k.Gr;
        this.Zl = k.Zl;
    }
}


Można nadpisać metodę ToString


public override string ToString()
    {
        return Zl + " zł i " + Gr + "gr";
    }


albo nadpisać operator + w taki sposób, że tworzy nowy obiekt z wartościami jako sumą


public static StanKonta operator +(StanKonta L, StanKonta P)
    {
        uint tmpZl = L.Zl + P.Zl;
        ushort tmpGr = (ushort)(L.Gr + P.Gr);

        uint zlotychWGroszach = (uint)(tmpGr / 100);
        tmpZl += zlotychWGroszach;
        tmpGr = (ushort)(tmpGr - zlotychWGroszach * 100);

        return new StanKonta(tmpZl, tmpGr);
    }


i jak damy:

Console.WriteLine(a+b);

to pojawienie się tego nowego obiektu w obszarze WriteLine uruchomi jego (nadpisaną) metodę ToString i wyświetli sumę jako ilość zł i gr w postaci opisanej w ToString



Jeśli chcemy używać obiektów w kolekcji nadpisujemy Equals(sprawdza czy to te same obiekty czy wskazują to samo miejsce pamięci )   i GetHashCode


public override bool Equals(object obj)
    {
        if (!(obj is StanKonta)) 
            return false;

        return (this == (StanKonta)obj);
    }
public override int GetHashCode()
    {
        return (this.Zl * 4 + this.Gr * 20).GetHashCode();
    }
}

w Equals pierwszy warunek sprawdza czy obj jest obiektem klasy StanKonta
jeśli tak nadpisujemy Equals któe tak naprawdę korzysta z naszej nadpisanego operatora ==

GetHashCode zwraca kod który jeśli identyczny znaczy, że to co braliśmy jest identyczne
Tutaj bierzemy Zl i Grosze i dajemy je jako argument do GetHashCode.
Po co mnożymy jedno x 4 drugie x 20 ?
Bo jeśli nie pomnożymy to wynik dla obiektu (5,10) bedzie taki sam jak dla (10,5)
dodajemy więc mnożenie przez dowolne wartości.




Własne operatory rzutowania 


Obiekt powyższej klasy możemy przypisać do zmiennej np typu float.


StanKonta a = new StanKonta(10, 5);
float f=a;

Jednak żeby to zrobić musimy zastosować rzutowanie a w kodzie klasy nadpisać operator rzutowania czego (obiekt klasy) na co (typ float).


public static explicit operator float(StanKonta value)
    {
        return value.Zl + (value.Gr / 100.0f);
    }
jak widać stosujemy zapis value.zmienna



polecenie explicit sprawi, że nadpisany operator uruchomi się tylko podczas jawnego rzutowania

f=(float)a;

Jesli chcemy żeby uruchomił się podczas niejawnego rzutowania stosujemy implicit

f=a;



Jeśli chcemy przypisać odwrotnie np do a wartość 5,99.

a = (StanKonta)5.99;

a w klasie:


public static explicit operator StanKonta(float value) //(StanKonta)value;
    {
        //ushort tmpGr = (ushort)((value - (uint)value) * 100);
        ushort tmpGr = Convert.ToUInt16((value - (uint)value) * 100);
        return new StanKonta((uint)value, tmpGr);

            //5.47 - 5 = 0.47 * 100 = 47
    }

czyli rzutowanie klasy na typ float, tu również explicit czyli tylko jawne

Konwersja ushort może spowodować utratę pewnej dokładności, dlatego Convert.ToUInt16





Wartość null dla typów wartościowych 


czyli do zmiennej typu int przypisujemy null, żeby to zrobić musimy dodać znak zapytania?
w metodzie (klasy) która będzie zwracać null do inta


public static int? getSomethingFromDataBase()
    {
        
        return null;

i wywołujemy metodę:

int? a = getSomethingFromDataBase();

to jest równoznaczne z wywołaniem:
Nullable<int> a = getSomethingFromDataBase();


dzięki null w typie wartościowym wiemy kiedy np nie wyświetlać zawartości


if (a == null)
            Console.WriteLine("nie podałeś a");
        else
            Console.WriteLine(a);


co ciekawe jeśli stworzymy int b to nie możemy przypisać do niego takiego a czyli int z nullem
musimy zastosować operator ??

int b = a   ??  0; 

oznacza, że przypisz wartość a do b tylko JEŻELI ISTNIEJE,
JEZELI NIE ISTNIEJE to PRZYPISZ TO CO JEST PO ?? w tym wypadku 0





Metody rozszerzające 


Jeśli tworzymy klasę i chcemy żeby dziedziczyła ze String to nie uda się, bo String jest zapieczętowany przed dziedziczeniem.
Aby stworzyć klasę która ma metodę sprawdzającą czy 1 litera jest duża, nie może ona dziedziczyć po string, ale w metodzie możemy sie posłużyć metodą klasy String  IsUpper i zwrócić jej wynik.


static class StringHelper
{/*
    public static bool IsCapitalized(String value)
    {
        return Char.IsUpper(value[0]);
    }
    */
    public static bool IsCapitalized(this String value)
    {
        return Char.IsUpper(value[0]);
    }
}

Char dzidziczy po String,  value[0] sprawdza pierwszą literę String value


Żeby wywołać


// Console.WriteLine(StringHelper.IsCapitalized(a));
        Console.WriteLine(a.IsCapitalized());


pierwsza zakomentowana część wywoływana jest tradycyjnie        Klasa.Metoda

druga odkomentowana to przykład rozszerzonej metody gdzie mamy dodane słówko this
public static bool IsCapitalized(THIS String value)

takiego wpisu możemy dokonać tylko w klasie statycznej dlatego StringHelper jest static

dzięki istnieniu takiej metody rozszerzonej mamy rozszerzoną Klasę String i dlatego możemy
zachować się tak jakby String miał taką metodę i wywołać bezpośrednio po kropce stringu a

a.NazwaRozszerzonejMetody()


Czyli możemy tym sposobem dodać metodę do klasy, i działa to nawet w klasach dziedziczonych:


class Test
{
}
class Test2 : Test
{
}

static class TestHelper
{
    public static bool tester(this Test value)
    {
        return true;
    }
}


i możemy w obiekcie klasy Test2 uzyc metody rozszerzonej


Test2 b = new Test2();
        Console.WriteLine(b.tester());




Wyjątki i ich obsługa  


try
{

}

Catch
{

}

Jeśli w bloku try trafi na wyjątek od razu skacze do catch, nie kontynuuje dalej bloku try.
Łapanie zawsze od szczegółowych do ogólnych. Dlatego w przykładzie a=9.
Gdyby było 10 to skoczył by do catch OurNameException


static void Main(string[] args)
    {
        int a = 9;
        string b;

        b = Console.ReadLine();  //odczytaj z klawiatury

        try
        {
            if (a == 10)
                throw new OurNameException("a było równe 10"); //rzucamy wlasny WYJATEK gdy ==10

            int tmp = Convert.ToInt32(b);  //zamien odczytane b na int32, jeśli text skacze do catch
            Console.WriteLine(a / tmp);    //podziel, jeśli 0 skacze do catch

            Console.WriteLine("lala");
        }
        catch (DivideByZeroException e)  //wyjatek gdy dzielimy przez 0
        {
            Console.WriteLine(e.Message);  //i wiadomość która temu towarzyszy 
        }
        catch (OurNameException e)  //łapanie naszego wyjątku gdy a==10
        {
            //tu jest to lepiej obsluzone jakies instrukcje
            Console.WriteLine("tu jest cos dopisane przez nas--> "+e.Message);
        }
        catch (Exception e) //najbardziej ogólny, złapiemy jeśli b było textem
        {
            Console.WriteLine(e.Message); //i nie dało się zamienić na int
        }
        finally
        {
            Console.WriteLine("ja pokaze sie zawsze, sluze do czyszczenia, zamykania plikow,");
        }

        Console.WriteLine("Kod po dzieleniu");

        Console.ReadLine();
    }


class OurNameException : Exception
{
    public OurNameException(string msg) : base(msg) //base wywołanie konstuktora bazowego Exception
    {
    }
}













Csh


blog o C# : cezarywalenciuk.pl


http://hilite.me/  Colorful