background image

 

 

Język C# 2.0

Szybki kurs

Wojciech Jaworski

background image

 

 

Język C#

• Najważniejszy język programowania dla platformy .NET

• Obiektowy, prosty, nowoczesny i bezpieczny

• Najbardziej wszechstronny, pozwala korzystać ze wszystkich możliwości 
platformy

• Łatwy do nauki – bardzo duże podobieństwo do Javy 

• Główny architekt – Anders Hejlsberg

• Obecna wersja 2.0 ukazała się w październiku 2005

• W przygotowaniu wersja 3.0

background image

 

 

System operacyjny (Win 32)

Message

Queueing

COM+

IIS

WMI

Common Language Runtime (CLR)

.NET Framework Class Library

ADO.NET i XML

Web Forms

Windows Forms

Web Services

C#

VB.NET

J#

C++

PERL, Python, ...

background image

 

 

Hello world!

using System;

class Hello
{

public static void Main()
{

Console.WriteLine("Hello world!");

}

}

background image

 

 

Co na początek warto wiedzieć o Console

• Podstawowy strumień wejścia-wyjścia w aplikacjach konsolowych

• Obsługuje czytanie i pisane, przemieszczanie kursora po konsoli, zmianę 
ustawień klawiatury a nawet ustawienie koloru tła i obsługę głośniczka 
systemowego

• Pozwala na formatowanie wyprowadzanego tekstu:

Console.WriteLine(

Liczba {0:F2} z dwiema cyframi po 

przecinku

, 12.34556");

• Formaty:

• C – format walutowy
• D – notacja dziesiętna, pozwala określić liczbę wyświetlanych cyfr
• E – notacja wykładnicza
• F – notacja zmiennoprzecinkowa z ustaloną liczbą miejsc po przecinku
• N – format liczbowy określony w Ustawieniach regionalnych
• X – format szesnastkowy

background image

 

 

Program w C#

• Aplikacja w C# to zbiór klas, struktur, interfejsów i wyliczeń

• Podstawowe elementy programu to klasy, zasady ich budowy bardzo 
podobne do C++ i Javy

• Punktem startowym aplikacji jest statyczna metoda Main()

• Klasa składa się z pól, metod i właściwości, oznakowanych 
modyfikatorami dostępu znanymi z innych języków  

• Aplikacja w C# może składać się z wielu plików

• Klasa w C# może być podzielona na wiele plików, jeden plik może 
zawierać wiele klas 

• Nazwa pliku nie musi być taka sama jak nazwa klasy

background image

 

 

Przestrzenie nazw

• Klasy .NET Framework Class Library są pogrupowane w przestrzenie 
nazw (namespaces)

• Namespace dla naszej aplikacji tworzymy umieszczając odpowiednie 
klasy w blokach namespace Nazwa {  …  }
• Odwołania do klasy:

• z podaniem pełnej „ścieżki” w przestrzeni nazw:

 System.Console.WriteLine();
• użycie using w nagłówku pliku:

 using System;

………

 Console.WriteLine();

background image

 

 

Wbudowane typy danych

background image

 

 

Podział typów danych

Typy bezpośrednie

• Wyliczenia i struktury

 Zmienne danego typu umieszczane na stosie

• Bezpośrednio zawierają swoje  dane
• Każda ma własną kopię swoich  danych
• Operacje na jednej nie mają wpływu na drugą

Typy referencyjne

• Obiekty umieszczane na stercie

• Zmienne zawierają referencje do swoich danych (do obiektu)
• Dwie zmienne mogą odnosić się do tego samego obiektu
• Operacje na jednej zmiennej mogą mieć wpływ na drugą

background image

 

 

Typy C# a typy CLS

Typ C#

Rozmiar

Typ CLS

bool

1

System.Boolean

byte

1

System.Byte

sbyte

1

System.SByte

short

2

System.Int16

ushort

2

System.UInt16

char

2

System.Char

int

4

System.Int32

uint

4

System.UInt32

float

4

System.Single

double

8

System.Double

long

8

System.Int64

ulong

8

System.UInt64

decimal

16

System.Decimal

object

4

System.Object

string

4

System.String

background image

 

 

Uwagi na temat typów bezpośrednich

• Zmienne tworzymy w standardowy sposób:

int a = 18;

• Przed pierwszym użyciem zmiennej trzeba jej przypisać wartość (nie 
dotyczy to pól klasy, którym są automatycznie inicjowane)

• Deklaracje stałych poprzedzamy słowem kluczowym const,  
readonly – zmienna, której tylko raz można przypisać wartość

• Konwersje do liczbowych typów „pojemniejszych” dokonują się 
automatycznie, w pozostałych przypadkach należy użyć operatora 
rzutowania

int i =10;   short s =(int)x;

• Brak niejawnej konwersji do typu logicznego jak w C

int i = 1;  if (i) {…}  //BŁĄD
if (i = 2) {…}

//BŁĄD

background image

 

 

Klasa Object

• W C# wszystko (a nie tylko prawie wszystko) jest obiektem

• Niektóre metody klasy Object:

  Equals()  GetType()  ToString()  MemberwiseClone()

• Wszystkie typy (w tym typy bezpośrednie) wywodzą się z Object
posiadają ponadto wiele własnych metod, poprawne są zatem konstrukcje:

int x = int.ParseInt(”12”);

25.ToString();

• Istnieje mechanizm automatycznego opakowywania wartości skalarnych 
do obiektów

int x =14; object o = x;

//OK

background image

 

 

Operatory uporządkowane względem priorytetu

( ) 

grupowanie lub wywołania metody

.  ->

 

operatory dostępu do składowych

[] 

operator indeksowania 

++ --

operatory inkrementacji i dekrementacji

new 

tworzenie nowego obiektu w pamięci

typeof, sizeof 

typ obiektu, rozmiar

! ~ 

negacja logiczna ( ! ) lub bitowa ( ~ ) 

operator dereferencji 

operator pobrania adresu

(typ)

 

operator rzutowania typu

* /   % 

operatory mnożenia, dzielenia oraz modulo

+ - 

operatory dodawania i odejmowania

>>  << 

przesunięcie bitowe w prawo lub w lewo 

>  <  >=  <= 

operatory relacji 

is 

operator zgodności typów

as 

operator rzutowania w dół hierarchii klas

==  != 

operatory równości i nierówności 

|  &  ^ 

operatory logiczne bitowe.

||  &&

operatory logicznej alternatywy oraz koniunkcji

?: 

trójargumentowy operator warunku 

= *= += -= %= &= ...  operatory przypisania

background image

 

 

Uwagi do operatorów

• Gdy operatory mają ten sam priorytet o ich kolejności decyduje łączność

• Operatory przypisań oraz jednoargumentowe łączą od prawej strony, 
natomiast operatory dwuargumentowe (z wyjątkiem przypisań) od lewej

• Ponadto istnieją operatory checked i unchecked służące do sprawdzania 
przepełnienia, np. 

int a = checked( b * c ) ;

background image

 

 

Instrukcje warunkowe

if ( Boolean-expression )

first-statement

else

second-statement

switch ( expression )
{
case value1: 

instr; break;

case value2: 

instr; break;


default

instr; break;

}

background image

 

 

Pętle

while (Boolean-expression )

{ blok instrukcji }

do
{ blok instrukcji }
while (Boolean-expression )

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

{ blok instrukcji }

ArrayList<int> numbers = new ArrayList <int>();
foreach (int number in numbers) 
    Console.WriteLine(number);

background image

 

 

Instrukcje skoku

• goto – skok do wybranego miejsca oznaczonego etykietą
• break – wyjście z pętli
• continue – natychmiastowe rozpoczęcie kolejnego kroku pętli
• return – zwrócenie wyniku przez metodę i jej zakończenie

background image

 

 

Preprocesor

• Dyrektywy w C# podobnie jak w C  pozwalają na preprocessing

 W porównaniu do C preprocesor C# ma mniejsze możliwości, nie 
obsługuje makr

• Zestaw dyrektyw obejmuje

#define #undef #if #else #elif #endif 

#pragma #warning #error #line #region

• Dyrektywa #region pozwala narzędziom takim jak Visual Studio na 

ukrywanie kodu w edytorze

background image

 

 

Wyliczenia

• Bardzo proste typy definiujące zbiór możliwych wartości

• Wartości są synonimami dla wartości całkowitych

• Definicja:

enum Color { Red, Green, Blue };

• Użycie:

Color myBest = Color.Green; 

background image

 

 

Tworzenie klasy

class BankAccount
{
    private decimal _balance;

//pola klasy

    private string _name;

    public BankAccount(string name)

//konstruktor

{  _name = name; }

    public void Withdraw(decimal amount)

     //metody

    { ... }
    public void Deposit(decimal amount)
    { ... }

    public string Name {

//właściwości

get { return _name; }
set { _name = value; } }

}

background image

 

 

Modyfikatory dostępu

• public : składowa lub typ zadeklarowany jako publiczny są dostępne z 
dowolnego miejsca. Ten rodzaj dostępu jest domyślny dla interfejsów

 private : składowa zadeklarowana jako prywatna jest dostępna tylko z 
wnętrza typu, w którym została zadeklarowana. Ten rodzaj dostępu jest 
domyślny dla składowych klas i struktur

 protected : składowa zadeklarowana jako chroniona jest dostępna z 
wnętrza klasy, w której została zadeklarowana lub z wnętrza klasy 
pochodnej

 internal : typ lub składowa typu są dostępne tylko z wnętrza pakietu, w 
którym nastąpiła ich deklaracja

 protected internal : składowa zadeklarowana z takim rodzajem dostępu 
jest widoczna z wnętrza klasy, w której została zadeklarowana (lub klasy 
pochodnej od niej) oraz z wnętrza pakietu, w którym się znajduje

background image

 

 

Metody

• Metody mogą być statyczne tj. wykonywane na rzecz klasy, w C# w 
przeciwieństwie do Javy nie można wywołać statycznej metody na obiekcie

• Klasa może posiadać przeciążone metody – o tej samej nazwie, różniące 
się sygnaturą (na sygnaturę nie mają wpływu nazwy parametrów a tylko ich 
typy, ani typ zwracany przez procedurę)

• Argumenty procedury mogą być przekazywane przez wartość, przez 
referencję lub jako parametry wyjściowe:

public static void DoSomething(string param1, 

ref string param2, out string param3);

background image

 

 

Właściwości (properties)

• Popularny sposób enkapsulacji w C#, właściwości zapewniają podobny 
dostęp jak pola

• get dostęp do odczytu

 set dostęp do zapisu

• Właściwości to „logiczne pola” – wartość zwracana przez accessor get 
może być obliczana

• Properties to nie wartości; nie mają adresu

• Properties nie mogą być użyte jako parametry ref lub out metod

• Semantycznie są podobne do metod, mogą być virtualabstract lub 
override, ale nie mogą być typu void ani posiadać parametrów

background image

 

 

Tworzenie obiektu

• Każda klasa posiada metodę tworzącą nowy egzemplarz klasy – 
konstruktor. Nazwa konstruktora jest taka sama jak nazwa klasy.

• Klasa może posiadać wiele konstruktorów różniących się typem i liczbą 
parametrów. Brak konstruktora w definicji klasy powoduje utworzenie przez 
kompilator domyślnego bezparametrowego konstruktora

• Jeżeli napiszesz swój własny konstruktor, kompilator nie stworzy 
konstruktora domyślnego

• Tworzenie obiektu – operator new zwraca referencję do obiektu 

utworzonego przez konstruktor

BankAccount yours = new BankAccount(” Kowalski” );

background image

 

 

Zmienne referencyjne

• Do obiektów odwołujemy się zawsze przez referencje

• Wiele referencji może wskazywać na ten sam obiekt, po utracie ostatniej 
referencji obiekt zostanie po pewnym czasie skasowany przez GC

• W przypadku zmiennych referencyjnych operatory działają na 
referencjach a nie wartościach obiektów

• Aby porównać zawartość obiektów zamiast == należy użyć metody 
Equals()

 Tworzenie płytkiej kopii – metoda MemberwiseClone()

 Gdy w skład naszego obiektu wchodzą inne zmienne referencyjne dla 
prawidłowego działania tych i innych metod klasy Object, musimy je 
najczęściej w naszej klasie przedefiniować

background image

 

 

Operator as

• Działa dla typów referencyjnych jak rzutowanie

• W przypadku błędu

• zwraca null

• nie wyrzuca wyjątku

Bird b = a as Bird; // Convert

if (b == null)

Console.WriteLine("Not a bird");

background image

 

 

Operator is

• Zwraca true, jeżeli można wykonać konwersję

Bird b;
if (a is Bird)
    b = (Bird) a; // Safe

else

    Console.WriteLine("Not a Bird");

background image

 

 

Operator this

• this odwołuje się do obiektu, na którym wywołano metodę

class BankAccount
{   
    ...
    public void SetName(string name)
    {
        this._name = name;
    }
    private string _name; 
}

background image

 

 

Statyczne klasy i konstruktory

• Oznaczenie klasy jako statyczną zabrania tworzenia obiektów tej klasy

• Klasy statyczne nie mogą zawierać niestatycznych składników ani 
konstruktora

• Konstruktor statyczny może zostać umieszczony w dowolnej klasie, 
zostanie wywołany przed wywołaniem właściwego konstruktora

• Konstruktor statyczny nie może posiadać modyfikatorów dostępu

background image

 

 

Destruktory

• Destruktor to metoda, która zostanie wywołana tuż przed usunięciem 
obiektu z pamięci

• Sam destruktor nie zajmuje się zwalnianiem pamięci, ale może np. 
zamknąć otwarte pliki

• O momencie wywołania destruktora decyduje Garbage Collector 

• Definicja destruktora:

~NazwaKlasy { … }

• Brak modyfikatorów dostępu

background image

 

 

Wcześniejsze zwalnianie zasobów

• Możemy zażądać wcześniejszego zwolnienia zasobów implementując 
interfejs IDisposable – umieścić w klasie definicję metody Dispose()

• Należy wtedy wyłączyć mechanizm automatycznego odzyskiwania 
pamięci metodą GC.SuppressFinalize(this) w ciele metody Dispose()

• Instrukcja using – automatyczne wywołanie Dispose() po bloku instrukcji

using ( Font f = new Font(„Arial”) )
{

}

//tu wykona się f.Dispose()

background image

 

 

Dziedziczenie w C#

• Składnia:

class KlasaOgolniejsza
{   
    ...
}
class KlasaSzegolowa: KlasaOgolniejsza

{
    ...
}

• Klasa dziedzicząca nie może być bardziej dostępna niż podstawowa

• Brak wielodziedziczenia, rozwiązanie: interfejsy

background image

 

 

Dziedziczenie a konstruktory

• W deklaracji konstruktora z parametrami musi być użyte słowo base

class Token
{
    protected Token(string name) { ... }
    ...
}
class CommentToken: Token
{
    public CommentToken(string name) : base(name) { } 
    ...
}

background image

 

 

Metody wirtualne

• Metody wirtualne są polimorficzne 

• Składnia:

class Token
{   ...
    public virtual string Name( ) { ... } 
}

class CommentToken: Token
{   ...
    public override string Name( ) { ... }
}

background image

 

 

Zasady nadpisywania

• Trzeba dopasować metodę nadpisaną do jej metody wirtualnej

• Można nadpisać nadpisaną metodę

• Nie wolno deklarować metody override jako virtual

• Nie wolno deklarować metody override jako static lub private

class Token
{   ...
    public int LineNumber( )  { ... }
    public virtual string Name( ) { ... } 
}
class CommentToken: Token
{   ...
    public override int LineNumber( ) { ... } 

//BLAD

    public override string Name( ) { ... }

 

//OK

private override string Name( ) { ... }

 

//BLAD

public override string Name(int k) { ... }

//BLAD

}

background image

 

 

new do ukrywania metod

• Do unikania kolizji nazw

• Składnia:

class Token
{   ...
    public int LineNumber( )  { ... }
}

class CommentToken: Token
{   ...
    new public int LineNumber( )  { ... }
    
}

background image

 

 

Zagadka

• Co wyświetli program?

class A {
   public virtual void M() { Console.Write("A"); }
}
class B: A {
   public override void M() { Console.Write("B"); }
}
class C: B {
   new public virtual void M() { Console.Write("C"); }
}
class D: C {
   public override void M() { Console.Write("D"); }
   static void Main() {
       D d = new D(); C c = d; B b = c; A a = b;
       d.M(); c.M(); b.M(); a.M();
   }
}

background image

 

 

Metody abstrakcyjne

• Sygnatura metody poprzedzona słowem abstract
• Metody abstrakcyjne nie posiadają ciała

• Tylko klasy abstrakcyjne mogą deklarować abstrakcyjne metody

• Metody abstrakcyjne są wirtualne

• Metody nadpisane mogą nadpisywać metody abstrakcyjne na kolejnych 
poziomach dziedziczenia

background image

 

 

Klasy abstrakcyjne i zamknięte

• Jeżeli klasę oznaczymy modyfikatorem abstract nie będzie możliwe 

tworzenie obiektów tej klasy

• Klasy abstrakcyjne mogą zawierać metod zwykłe lub abstrakcyjne

• Klasy zamknięte (sealed) uniemożliwiają dziedziczenia
• Wiele klas .NET Framework jest sealed: String, StringBuilder itd.

background image

 

 

Interfejsy

• Interfejs to „klasa”, która zawiera tylko deklaracje metod

• Metody nie posiadają modyfikatorów dostępu, nie mają ciała

• Klasa może implementować wiele interfejsów

• Konwencja: nazwa rozpoczyna się od ‘I’

• Przykład:

interface IToken
{
    int LineNumber( );
    string Name( );
}
class Token: IToken
{
int LineNumber( ) { cialo_metody }
 ... }

background image

 

 

Klasy abstrakcyjne a interfejsy

Podobieństwa

• Nie mogą mieć instancji

• Nie mogą być sealed

Różnice

• Interfejsy nie mogą zawierać implementacji

• Interfejsy nie mogą mieć składowych niepublicznych

• Interfejsy nie mogą rozszerzać nie-interfejsów

background image

 

 

Struktury

• Typy bardzo podobne do klas, mogą zawierać pola, metody, właściwości, 
konstruktory, tworzone za pomocą new

• Zmienne tych typów są bezpośrednie a nie referencyjne, zalecany w 
przypadku małych typów

• Nie umożliwiają dziedziczenia, nie mogą zawierać bezparametrowych 
konstruktorów (jeśli konstruktora brak nastąpi inicjalizacja wszystkich pól 
wartościami domyślnymi)

struct Point {
int x, y;
public Point(x, y) { 

this.x=x; 
this.y=y; 
}  

}

background image

 

 

Obsługa wyjątków

• Znana konstrukcja „try … catch … finally”

try

{

Console.WriteLine(„Podaj pierwszą liczbę");
int i = int.Parse(Console.ReadLine());
Console.WriteLine(„Podaj drugą liczbę");
int j = int.Parse(Console.ReadLine());
int k = i / j;

catch (OverflowException caught) 

{Console.WriteLine(caught.Message);}

catch (DivideByZeroException caught.Message) 

{Console.WriteLine(caught.Message);} 

catch (Exception caught.Message)

{Console.WriteLine(caught.Message);}

finally
{

Console.WriteLine(„Ten kod wykona się zawsze”);
Console.ReadLine();

}

background image

 

 

Wyrzucanie wyjątków

• Użytkownik może wyrzucić wyjątek za pomocą instrukcji:

• throw new <klasa wyjątku>
•throw new OutOfMemoryException();

• Użytkownik może zdefiniować swój własny wyjątek. Klasa wyjątku musi 
dziedziczyć w sposób bezpośredni lub pośredni z klasy System.Exception

• Brak słowa kluczowego throws do umieszczenia w sygnaturze metody

background image

 

 

Tablice

• Każdy typ tablicowy jest typem referencyjnym wywodzącym się z 
System.Array 

• Tworzenie prostych tablic

int[] tablInt = new int[ 100 ] ; 
int[ ] tabl = { 1, 2 , 3 } ;

 

• Tworzenie tablic typów referencyjnych wymaga inicjalizacji wartości tablicy

• Możliwe jest tworzenie tablic wielowymiarowych oraz nieregularnych

int [,] tabl3D = new int[5,10]; //Regularna tablica 
int [][] tabl2D = new int[10][] ; //Tablica nieregularna

 

background image

 

 

Metody dostępne dla tablic

• Rank – zwraca ilość wymiarów tablicy

• Length – zwraca ilość pól tablicy

• GetLenght() – zwraca liczbę pól danego wymiaru

• Sort() – sortuje tablice

 BinarySearch() – szybkie wyszukiwanie w posortowanych tablicach

• Clear() – ustawia wartości elementów tablicy na zero lub null

• Clone() – klonuje daną tablice

• IndexOf() – zwraca indeks pierwszego wystąpienia danej wartości w 
tablicy

background image

 

 

Tablice argumentów

• Słowo kluczowe params pozwala przekazać do metody dowolną liczbę 
argumentów bez konieczności jawnego tworzenia tablicy

public void DisplayParams(params int[] vals)
{

foreach (int i in vals)

Console.WriteLine(i);

}
...

DisplayParams(1,2,3,4,5);

background image

 

 

Mechanizm indeksowania

• Pozwala na dostęp do obiektu tak, jakby był on tablicą

• Przypuśćmy, że stworzyliśmy graficzną kontrolkę MyComboBox, 
przechowującą tablice stringów:

class MyComboBox
{

private string[] strings;
...
public string this[int index]
{
get { return strings[index]; }

// OBSLUZYC 

set { strings[index] = value; }  }

// WYJATKI!

}
...
MyComboBox box = new MyComboBox();
...
string s = box[2];

background image

 

 

Kolekcje

• C# i .NET Framework dostarczają wielu standardowych kolekcji:

 List<T>

 Queue<T>

 Stack<T>

 Dictonary<T,K>

 typ T kolekcji może być dowolnym typem referencyjnym lub bezpośrednim

• po kolekcjach możemy w wygodny sposób iterować instrukcją foreach 

lub skorzystać z enumeratorów

background image

 

 

Interfejsy kolekcji

• Możemy tworzyć własne typy kolekcji implementując w naszej klasie 
interfejsy kolekcji:

ICollection<T>  IEnumerable<T>  IComparable<T>

 IList<T>  IDictonary<T>

class IterowanaKlasa :IEnumerable<int>
{

private int[] tab = new int[10];
public IEnumerator<int> GetEnumerator()
{

foreach (int i in tab)

yield return i;

}

}
...
IterowanaKlasa klasa = new IterowanaKlasa();
foreach (int i in klasa)

Console.WriteLine(i);

background image

 

 

Łańcuchy znaków

• Łańcuchami nazywamy literały znakowe umieszczone w cudzysłowach 
oraz obiekty klasy string

• W zapisie literałów możemy używać znanych z C sekwencji sterujących

• Znak ‘@’ przed łańcuchem powoduje traktowanie dosłowne wszystkich 
znaków

• Obiekty klasy string są niezmienne

• Klasa string oferuje wiele użytecznych metod narzędziowych:

 Compare()  Concat()  Join()  CompareTo()  Insert() 

LastIndexOf()  Remove()  Split()  Substring()

ToCharArray() ToLower()  ToUpper() Trim()

background image

 

 

Manipulowanie łańcuchami

• Operatory (np. +=) i metody manipulujące łańcuchami zwracają zawsze 

nowy łańcuch, gdyż obiekty string są niezmienne

• Tworzenie obiektów jest czasochłonne, więc jeśli chcemy często 
manipulować łańcuchem dobrze jest użyć klasy StringBuilder, oferującej 
metody: Append(), AppendFormat(), Insert(), Remove(), Replace()

String ala = „Ala ma kota”;
StringBuilder builder = new StringBuilder();
foreach (string s in ala.Split(‘ ‘))

builder.AppendFormat(

Wyraz: {0}\n”, s);

Console.WriteLine(builder.ToString());

background image

 

 

Wyrażenia regularne

• C# obsługuje wyrażenia regularne o składni regexp z Perla 5

• Podstawową klasą do obsługi wyrażeń regularnych jest Regex

• Prosty przykład:

String ciag = "gk4435g52hjvj4hv";
Regex reg = new Regex("[0-9]+");
MatchCollection maches = reg.Matches(ciag);
foreach (Match mach in maches)
     Console.WriteLine(mach.ToString());
if (reg.IsMatch("15454621"))
     Console.WriteLine("Goal");

background image

 

 

A to jeszcze nie koniec

• Atrybuty

• Przeciążanie operatorów

• Nullable types

• Wątki i mechanizmy synchronizacji

• Domeny aplikacji

• Refleksja typów

• Delegaty i zdarzenia

• Wskaźniki i praca z kodem niezarządzanym