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 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 { } }
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; } }
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); }
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
