Professional Documents
Culture Documents
PRZYKADOWY ROZDZIA
SPIS TRECI
KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG
Visual C# 2005.
Zapiski programisty
Autor: Jesse Liberty
Tumaczenie: Przemysaw Szeremiota
ISBN: 83-246-0249-6
Tytu oryginau: Visual C# 2005: A Developers Notebook
Format: B5, stron: 280
Przykady na ftp: 1162 kB
TWJ KOSZYK
DODAJ DO KOSZYKA
CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK
CZYTELNIA
FRAGMENTY KSIEK ONLINE
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
Spis treci
Spis treci
Spis treci
ROZDZIA 1.
C# 2.0
W tym rozdziale:
Tworzenie typowanych list za pomoc kolekcji generycznych
Wasne kolekcje generyczne
Implementacja interfejsw kolekcji
Stosowanie iteratorw generycznych
Implementacja GetEnumerator dla zoonych struktur danych
Upraszczanie kodu metody anonimowe
Ukrywanie kodu typy czciowe
Tworzenie klas statycznych
Wyraanie wartoci pustych typami nullable
Odwoania do obiektw z globalnej przestrzeni nazw
Ograniczanie dostpu do waciwoci
Elastyczno delegacji z kowariancj i kontrawariancj
Pierwszy rozdzia bdzie prezentowa nowe cechy jzyka C# w wersji
2.0 mowa bdzie o typach generycznych, iteratorach, metodach anonimowych, typach czciowych, klasach statycznych, typach wyrniajcych wartoci puste (nullable) oraz ograniczaniu dostpu do waciwoci; omwienie obejmie te kowariancj i kontrawariancj delegacji.
21
Najbardziej chyba wyczekiwan cech jzyka C# w wersji 2.0 s jednak typy generyczne, dajce moliwo szybkiego tworzenia kolekcji. I od
nich wanie zaczniemy.
Rozdzia 1: C# 2.0
kolekcji obiektw Employee, prba rzutowania obiektu String na typ Employee sprowokuje wyjtek.
Wreszcie kolekcje platformy .Net 1.x utrudniay przechowywanie wartoci typw prostych (ang. value type). Wartoci typw prostych musiay
by przed wstawieniem do kolekcji pakowane do obiektw, a po wyuskaniu z kolekcji rozpakowywane z obiektw.
W .NET 2.0 wszystkie te problemy zostay wyeliminowane przez udostpnienie nowej biblioteki kolekcji generycznych, zagniedonej w przestrzeni nazw System.Collections.Generic. Ot kolekcja generyczna to
po prostu taka kolekcja, ktra pozwala na ujcie w deklaracji typw elementw przechowywanych. Dziki temu kompilator znajcy deklaracj
bdzie zezwala na wstawianie do kolekcji jedynie obiektw waciwego typu.
Kolekcje generyczne definiuje si z uyciem specjalnej skadni; w nawiasach ostrych wymienia si nazw typu, ktry musi zosta zdefiniowany przy deklarowaniu egzemplarza kolekcji.
Nie trzeba ju rzutowa obiektw wyuskiwanych z kolekcji generycznej,
a sam kod korzystajcy z takich kolekcji jest bezpieczniejszy, bo podlega
statycznemu (realizowanemu w czasie kompilacji) typowaniu; atwiej poddaje si konserwacji i prociej si go stosuje.
Jak to zrobi?
Aby oswoi si z nowymi typami generycznymi w .NET 2.0, najlepiej sprbowa samemu utworzy typowan klas kolekcji (List) przechowujc
elementy typu Employee (pracownik). wiczenie naley rozpocz od uruchomienia rodowiska Visual Studio 2005, utworzenia nowej aplikacji konsolowej jzyka C# i opatrzenia jej nazw CreateATypeSafeList. Kod generowany w ramach szkieletu aplikacji przez kreator Visual Studio 2005
naley zastpi kodem z listingu 1.1.
Kolekcje generyczne
czyni kod
bezpieczniejszym,
upraszczaj jego
konserwacj
i stosowanie.
WSK A ZWK A
Korzystanie z typw generycznych wymaga wcignicia do programu
przestrzeni nazw System.Collections.Generic. W Visual Studio 2005
jest to domylne zachowanie dla wszystkich tworzonych projektw.
23
// Klasa testujca
public class Program
{
// punkt wejciowy
static void Main()
{
// Deklaracja listy typowanej (obiektw typu Employee)
List<Employee> empList = new List<Employee>();
// Deklaracja drugiej listy typowanej (wartoci cakowitych)
List<int> intList = new List<int>();
// wypenienie list
for (int i = 0; i < 5; i++)
{
empList.Add(new Employee(I + 100));
intList.Add(i * 5);
// empList.Add(i * 5);
// patrz punkt "A co"
}
// wypisanie elementw listy wartoci cakowitych
foreach (int i in intList)
{
Console.Write("{0} ", i.ToString());
}
Console.Write("\n");
24
Rozdzia 1: C# 2.0
Wynik:
0 5 10 15 20
100 101 102 103 104
WSK A ZWK A
Kod rdowy poszczeglnych wicze mona pobra z serwerw wydawnictwa Helion, spod adresu ftp://ftp.helion.pl/przyklady/vc25za.zip.
Kod publikowany jest w postaci spakowanego archiwum. Rozpakowanie
archiwum zaowocuje utworzeniem szeregu katalogw odpowiadajcych
poszczeglnym rozdziaom, a w nich podkatalogw o nazwach zgodnych z nazwami poszczeglnych projektw. Na przykad kodu z listingu 1.1 naley szuka w katalogu r1\CreateATypeSafeList.
Jak to dziaa?
Kod z listingu 1.1 utworzy dwie klasy: klas Employee (pracownik) klas obiektw przechowywanych w kolekcji, oraz klas Program tworzon
przez kreator Visual Studio 2005. Do tego w programie wykorzystana zostaa klasa List z biblioteki klas .NET Framework Class Library.
Klasa Employee zawiera pojedyncze prywatne pole (empID), konstruktor
i metod przesaniajc metod ToString i wywietlajc cig zawierajcy warto pola empID.
W klasie Program tworzony jest egzemplarz klasy List majcy przechowywa obiekty klasy Employee. Typem empList jest wic kolekcja List
obiektw Employee. Std deklaracja kolekcji:
List<Employee> empList
25
WSK A ZWK A
Cao dziaa dokadnie tak, jak w instrukcji:
Dog milo = new Dog();
tworzcej egzemplarz klasy Dog na stercie i przypisujcej go do referencji do typu Dog o nazwie milo.
Od tego momentu mona rozpocz wypenianie listy wartoci cakowitych wartociami cakowitymi, a listy elementw typu Employee obiektami
klasy Employee. Po wypenieniu list mona w ptlach foreach przejrze listy,
wyuska poszczeglne elementy i wypisa ich wartoci na konsoli:
foreach (Employee employee in empList)
{
Console.Write("{0} ", employee.ToString());
}
A co
kiedy do listy obiektw Employee dodana zostanie warto cakowita?
C, trzeba sprbowa. Wystarczy usun znacznik komentarza z prezentowanego poniej wiersza z listingu 1.1 i sprbowa ponownie skompilowa program:
empList.Add(i * 5);
Rozdzia 1: C# 2.0
Error
1
The best overloaded method match for
'System.Collections.
Generic.List<ListCollection.Employee>.Add(ListCollection.Employee)' has
some
invalid arguments
Error
2
Argument '1': cannot convert from 'int' to
'ListCollection.Employee'
W kolekcjach
typowanych mona
umieszcza elementy
typw pochodnych
wobec typu
deklarowanego.
Kolekcja elementw
typu Employee moe
wic przechowywa
rwnie elementy
typu Manager, o ile
Manager jest typem
pochodnym
Employee.
Wicej informacji
Kompletu informacji o klasach generycznych w .NET 2.0 naley szuka
w pomocy MSDN, pod hasem Commonly Used Collection Types; warto
te zapozna si z artykuem publikowanym w witrynie ONDotnet.com
(OReilly) pod adresem http://www.ondotnet.com/pub/a/dotnet/2004/05/17/
liberty.html.
WSK A ZWK A
Artykuy i publikacje, na ktre powouj si w kolejnych wiczeniach,
s wymienione na stronie WWW ksiki pod adresem http://www.LibertyAssociates.com (odnonik Books, a nastpnie pozycja C# 2005:
A Developers Notebook).
27
Nastpne wiczenie bdzie ilustrowao sposb tworzenia wasnej typowanej kolekcji, uzupeniajcej zbir kolekcji udostpnianych w bibliotece
klas platformy .NET.
Od czasu do czasu
przyjdzie Ci
zdefiniowa
wasn klas kolekcji
generycznych.
Platforma .NET 2.0 udostpnia programistom szereg klas kolekcji generycznych implementujcych typowane listy, stosy, kolejki, sowniki i tym
podobne. Tak bogaty zestaw zaspokaja zdecydowan wikszo potrzeb
programistw w tym zakresie. Zamy jednak, e stoimy w obliczu koniecznoci utworzenia wasnej klasy kolekcji generycznej, uwzgldniajcej
wiedz z dziedziny danego problemu czy przejawiajcej specjalne cechy
(na przykad optymalizujcej rozmieszczenie elementw celem przyspieszenia odwoa). Na szczcie i sam jzyk, i platforma udostpniaj narzdzia
i mechanizmy tworzenia wasnych klas kolekcji generycznych.
Jak to zrobi?
Najprostszym sposobem powoania do ycia wasnej klasy kolekcji generycznej jest utworzenie danej kolekcji (np. kolekcji wartoci cakowitych)
i potem zastpienie nazwy typu int uoglnionym symbolem typu, na
przykad T.
Mianowicie:
private int data;
ma przyj posta:
private T data;
Tak definiuje si nowy typ wze Node typu T; jeli w definicji obiektu
miejsce T zajmie int, dla rodowiska wykonawczego obiekt ten bdzie typu
wze Node typu int (analogicznie tworzy si wzy dowolnego innego
typu).
28
Rozdzia 1: C# 2.0
WSK A ZWK A
Wielu programistw stosuje wygodny w zapisie symbol typu T, ale
Microsoft zaleca stosowanie duszych, bardziej opisowych symboli
(na przykad Node<DocumentType>).
Listing 1.2 prezentuje przykadowy kod tworzcy list wzw typu T i nastpnie powoujcy do ycia dwa egzemplarze takich list dla rnych waciwych obiektw wzw.
Listing 1.2. Wasna kolekcja generyczna
using System;
namespace GenericLinkedList
{
public class Pilgrim
{
private string name;
public Pilgrim(string name)
{
this.name = name;
}
public override string ToString()
{
return this.name;
}
}
public class Node<T>
{
// pola skadowych
private T data;
private Node<T> next = null;
// konstruktor
public Node(T data)
{
this.data = data;
}
// waciwoci
public T Data { get { return this.data; } }
public Node<T> Next
{
get { return this.next; }
}
// metody
public void Append(Node<T> newNode)
29
{
if (this.next == null)
{
this.next = newNode;
}
else
{
next.Append(newNode);
}
}
public override string ToString()
{
string output = data.ToString();
if (next != null)
{
output += ", " + next.ToString();
}
return output;
}
// koniec klasy
node.Next;
++ctr;
} // koniec while
throw new ArgumentOutOfRangeException();
30
Rozdzia 1: C# 2.0
} // koniec get
// koniec indeksera
// konstruktor
public LinkedList()
{
}
// metody
public void Add(T data)
{
if (headNode == null)
{
headNode = new Node<T>(data);
}
else
{
headNode.Append(new Node<T>(data));
}
}
public override string ToString()
{
if (this.headNode != null)
{
return this.headNode.ToString();
}
else
{
return string.Empty;
}
}
}
class Program
{
static void Main(string[] args)
{
LinkedList<int> myLinkedList = new LinkedList<int>();
for (int i = 0; i < 10; i++)
{
myLinkedList.Add(i);
}
31
Pilgrim d = pilgrims[1];
Console.WriteLine("Drugi pielgrzym to " + d);
}
}
}
Wynik:
Liczby: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Pielgrzymi: Rycerz, Mynarz, Szeryf, Kucharz
Czwarta liczba to 3
Drugi pielgrzym to Mynarz
Jak to dziaa?
Ot wanie powstaa generyczna kolekcja lista. Kolekcja podlegajca
typowaniu mona deklarowa jej egzemplarze dla dowolnych typw.
Najatwiej skonstruowa list tego rodzaju, bazujc na implementacji zwykej (nie generycznej) listy dla konkretnego typu danych. Ten konkretny
przykad przy definiowaniu listy definiuje list generyczn, ktrej czoo
(wze czoowy) jest inicjalizowany wartoci pust:
public class LinkedList<T>
{
private Node<T> headNode = null;
...
}
Tworzenie kolekcji
z typami
generycznymi jest
doprawdy bardzo
proste. Bo c
prostszego, ni
zaimplementowa
kolekcj dla dowolnie
wybranego typu,
a nastpnie zastpi
nazw typu
parametrem typu
generycznego <T>.
32
Kiedy do listy dodawane s elementy, tworzony jest obiekt nowego wza; w obliczu braku wza czoowego w nowy wze peni rol czoa listy;
jeli czoo listy jest ju zajte, nowy wze jest doczany do listy metod
Append wza czoowego.
Przy dodawaniu nowego wza do listy nastpuje przegld wzw skadajcych si na list; szukany jest wze ostatni, czyli taki, ktry we wskaniku nastpnego wza (pole next) przechowuje warto pust. Po jego
odnalezieniu do pola next przypisywana jest referencja nowego wza.
Lista LinkedList zostaa celowo zadeklarowana z identycznym parametrem typu jak Node. Obie stosuj w roli symbolu generycznego parametru
typu t sam liter (T), dziki czemu kompilator wie, e typ podstawiany
w miejsce T w definicji LinkedList ma rwnie zastpowa T w definicji
Node. To w zasadzie oczywiste: przecie wzami listy wartoci cakowitych powinny by wzy wartoci cakowitych.
Rozdzia 1: C# 2.0
A co
ze stosowaniem typw generycznych w innych strukturach? Czy to
moliwe?
Owszem, typy generyczne mona stosowa w strukturach, interfejsach,
delegacjach, a nawet w metodach.
Wicej informacji
Szczegowych informacji o tworzeniu wasnych klas z udziaem typw
generycznych naley szuka w pomocy MSDN pod hasem Topic: Generics, ewentualnie we wspomnianym ju artykule z witryny ONDotnet.com
(OReilly), http://www.ondotnet.com/pub/a/dotnet/2004/05/17/liberty.html.
Warto te zapozna si z projektem majcym na celu gromadzenie i udostpnianie bibliotek klas dla platformy .NET, o ktrym wicej na stronie
Wintellect (http://www.wintellect.com/powercollections/).
33
Jak to zrobi?
Interfejs IComparable jest ju zaimplementowany dla typu Integer; w prosty sposb mona tak implementacj utworzy rwnie dla klasy Pilgrim. Trzeba jedynie zmodyfikowa definicj klasy Pilgrim, tak aby sygnalizowaa implementacj interfejsu IComparable<T>:
public class Pilgrim: IComparable<Pilgrim>
Zakres typw
waciwych dla typu
generycznego mona
ogranicza.
Teraz wystarczy ju zmieni logik kodu odpowiedzialnego za dodawanie nowych wzw do listy. Tym razem zamiast szuka ostatniego wza listy, naleaoby szuka odpowiedniego miejsca pomidzy istniejcymi
wzami takiego, eby po wstawieniu nowego wza zachowane zostao uporzdkowanie listy; miejsce to wyszukuje si na bazie implementacji CompareTo.
Przede wszystkim typ przechowywany w wle musi implementowa interfejs IComparable. Osiga si to przy uyciu sowa kluczowego where:
public class Node<T> : IComparable<Node<T>> where T:IComparable<T>
34
Rozdzia 1: C# 2.0
namespace ImplementingGenericInterfaces
{
public class Pilgrim : IComparable<Pilgrim>
{
private string name;
public Pilgrim(string name)
{
this.name = name;
}
public override string ToString()
{
return this.name;
}
// implementacja interfejsu
public int CompareTo(Pilgrim rhs)
{
return this.name.CompareTo(rhs.name);
}
public bool Equals(Pilgrim rhs)
{
return this.name == rhs.name;
}
}
35
36
Rozdzia 1: C# 2.0
}
}
public override string ToString()
{
string output = data.ToString();
if (next != null)
{
output += ", " + next.ToString();
}
return output;
}
}
// koniec klasy
// waciwoci
// indekser
public T this[int index]
{
get
{
int ctr = 0;
Node<T> node = headNode;
while (node !=
{
if (ctr ==
{
return
}
else
{
node =
}
node.Next;
++ctr;
} // koniec while
throw new ArgumentOutOfRangeException();
} // koniec get
// koniec indeksera
// konstruktor
public SortedLinkedList()
37
{
}
// metody
public void Add(T data)
{
if (headNode == null)
{
headNode = new Node<T>(data);
}
else
{
headNode = headNode.Add(new Node<T>(data));
}
}
public override string ToString()
{
if (this.headNode != null)
{
return this.headNode.ToString();
}
else
{
return string.Empty;
}
}
}
class Program
{
// punkt wejcia
static void Main(string[] args)
{
SortedLinkedList<int> mySortedLinkedList = new
SortedLinkedList<int>();
Random rand = new Random();
Console.Write("Wypenianie: ");
for (int i = 0; i < 10; i++)
{
int nextInt = rand.Next(10);
Console.Write("{0} ", nextInt);
mySortedLinkedList.Add(nextInt);
}
SortedLinkedList<Pilgrim> pilgrims = new
SortedLinkedList<Pilgrim>();
pilgrims.Add(new Pilgrim("Rycerz"));
pilgrims.Add(new Pilgrim("Mynarz"));
pilgrims.Add(new Pilgrim("Szeryf"));
pilgrims.Add(new Pilgrim("Kucharz"));
pilgrims.Add(new Pilgrim("Adwokat"));
38
Rozdzia 1: C# 2.0
Console.WriteLine("\nPobieranie kolekcji...");
DisplayList<int>("Liczby", mySortedLinkedList);
DisplayList<Pilgrim>("Pielgrzymi", pilgrims);
//Console.WriteLine("Liczby: " + mySortedLinkedList);
//Console.WriteLine("Pielgrzymi: " + pilgrims);
Console.WriteLine("Czwarta liczba to " + mySortedLinkedList[3]);
Pilgrim d = pilgrims[2];
Console.WriteLine("Trzeci pielgrzym to " + d);
//
//
//
//
}
// koniec klasy
// koniec przestrzeni nazw
Wynik:
Wypenianie: 2 8 2 5 1 7 2 8 5 5
Pobieranie kolekcji...
Liczby: 1, 2, 2, 2, 5, 5, 5, 7, 8, 8
Pielgrzymi: Adwokat, Kucharz, Mynarz, Rycerz, Szeryf
Czwarta liczba to 2
Trzeci pielgrzym to Mynarz
Jak to dziaa?
Klasa Pilgrim zostaa uzupeniona o implementacj interfejsu generycznego IComparable. Sama lista nie zmienia si ani na jot, ale ju klasa
wzw listy (Node) przesza powan metamorfoz, dziki ktrej wstawianie wzw do listy odbywa si z zachowaniem ich wzajemnego uporzdkowania.
Po pierwsze, klasa Node zostaa oznaczona jako klasa implementujca interfejs IComparable i ograniczona do przechowywania obiektw takich typw, ktre rwnie implementuj w interfejs:
public class Node<T> : IComparable<Node<T>> where T:IComparable<T>
39
A co
z wymaganiem implementacji interfejsu IComparable? Dlaczego musiaa go implementowa klasa Pilgrim i Node, a ju sama klasa kolekcji
(tu SortedLinkedList) nie?
Aby to wyjani, trzeba przypomnie, e i Pilgrim, i Node to obiekty danych podlegajce operacjom porwnania; sama lista jako oglniejsza struktura nie jest za nigdy porwnywana z innymi listami. Uporzdkowanie
wzw listy odbywa si przez ustalanie ich kolejnoci na bazie porwna; nigdzie nie zachodzi za porwnanie dwch list i sprawdzanie, ktra
z nich jest wiksza.
z przekazywaniem typw generycznych do metod? Czy to moliwe?
Tak, przekazywanie typw generycznych do metod jest dozwolone, pod
warunkiem e chodzi o metody generyczne. Przykadem moe by poniszy kod zaczerpnity z listingu 1.3, wywietlajcy zawarto listy liczb
i listy pielgrzymw:
Console.WriteLine("Liczby: " + myLinkedList);
Console.WriteLine("Pielgrzymi: " + pilgrims);
Nic nie stoi na przeszkodzie, aby utworzy metod przyjmujc za porednictwem argumentu tak list i wywietlajc jej elementy (albo w dowolny
inny sposb manipulujc t list):
40
Rozdzia 1: C# 2.0
WSK A ZWK A
Kompilator ma moliwo wnioskowania o typie metody na podstawie
typw argumentw, wic poprzednie dwa wywoania mona zapisa
rwnie tak:
DisplayList("Liczby", myLinkedList);
DisplayList("Pielgrzymi", pilgrims);
Wicej informacji
Zawarto przestrzeni nazw Generic jest wyczerpujco omawiana w dokumentacji MSDN wystarczy j przeszuka pod ktem hasa Systems.Collections.Generic. Polecam te artyku traktujcy o typach generycznych, publikowany w serwisie ONDotnet.com (OReilly) (http://www.
ondotnet.com/pub/a/dotnet/2004/05/17/liberty.html).
Dodawanie
iteratorw pozwala
klientom przeglda
kolekcje w ptlach
foreach.
41
Jak to zrobi?
Aby uproci sobie tworzenie iteratorw, naleaoby przede wszystkim
uproci klasy Pilgrim i LinkedList. Klasa kolekcji LinkedList powinna
te zaniecha stosowania wzw i przechowywa elementy listy w tablicy o staym rozmiarze (taka tablica to najprostszy moliwy typowany
kontener-kolekcja). Kolekcja zostanie wic list jedynie z nazwy! To pozwoli si jednak skupi na implementacji interfejsu IEnumerable, prezentowanej na listingu 1.4.
Listing 1.4. Implementacja interfejsu IEnumerable (wersja uproszczona)
#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
#endregion
namespace SimplifiedEnumerator
{
// uproszczona wersja klasy Pilgrim
public class Pilgrim
{
private string name;
public Pilgrim(string name)
{
this.name = name;
}
public override string ToString()
{
return this.name;
}
}
// uproszczona wersja klasy listy
class NotReallyALinkedList<T> : IEnumerable<T>
{
42
Rozdzia 1: C# 2.0
class Program
{
static void Main(string[] args)
{
// rczne tworzenie tablicy obiektw klasy Pilgrim
Pilgrim[] pilgrims = new Pilgrim[5];
pilgrims[0] = new Pilgrim("Rycerz");
pilgrims[1] = new Pilgrim("Mynarz");
pilgrims[2] = new Pilgrim("Szeryf");
pilgrims[3] = new Pilgrim("Kucharz");
pilgrims[4] = new Pilgrim("Adwokat");
// utworzenie listy, przekazanie tablicy elementw
NotReallyALinkedList<Pilgrim> pilgrimCollection =
new NotReallyALinkedList<Pilgrim>(pilgrims);
// przegldanie elementw listy
foreach (Pilgrim p in pilgrimCollection)
{
Console.WriteLine(p);
}
}
}
}
43
Wynik:
Rycerz
Mynarz
Szeryf
Kucharz
Adwokat
Jak to dziaa?
W tym przykadzie lista zostaa znacznie uproszczona elementy listy
zamiast w przydzielanych dynamicznie wzach lduj w statycznej tablicy (co w zasadzie dyskwalifikuje t implementacj jako list). Poniewa
owa pseudo-lista daje si jednak przeglda z uyciem iteratorw, przechowywana w niej kolekcja obiektw Pilgrim daje si przeglda w ptli
foreach.
W obliczu zapisu:
foreach (Pilgrim p in pilgrimCollection)
Kade zastosowanie
ptli foreach
jest przez
kompilator
tumaczone
na wywoanie
metody
GetEnumerator.
kompilator jzyka C# wywouje na rzecz obiektu kolekcji metod GetEnumerator. Rozwinicie takiej ptli jest wewntrznie implementowane mniej
wicej tak:
Enumerator e = pilgrimCollection.GetEnumerator();
while (e.MoveNext())
{
Pilgrim p = e.Current;
}
Rozdzia 1: C# 2.0
A co
z implementacj metody GetEnumerator dla bardziej zoonych struktur
danych, na przykad dla prawdziwej listy?
To wanie bdzie przedmiotem nastpnego wiczenia.
Wicej informacji
Poruszone zagadnienie jest wyczerpujco omawiane w obszernym artykule z biblioteki MSDN, zatytuowanym Iterators (C#).
Implementacja GetEnumerator
dla zoonych struktur danych
Aby da pierwotnej licie LinkedList moliwo przegldania elementw za porednictwem iteratora, trzeba zaimplementowa interfejs IEnumerable<T> dla samej klasy listy (LinkedList) i klasy wzw listy (Node).
public class LinkedList<T> : IEnumerable<T>
public class Node<T> : IComparable<Node<T>>, IEnumerable<Node<T>>
Jak to zrobi?
Przy okazji poprzedniego wiczenia okazao si, e interfejs IEnumerable
wymaga zasadniczo implementacji tylko jednej metody GetEnumerator. Implementacj t dla bardziej zoonej struktury danych, jak jest
choby lista, prezentuje listing 1.5 (zmiany wzgldem kodu z listingu 1.3
zostay wyrnione pogrubieniem).
Sowo yield dziaa jak instrukcja return uzupeniona o pami stanu suy do iteracyjnego
zwracania kolejnych elementw sekwencji przyp. tum.
Implementacja GetEnumerator dla zoonych struktur danych
45
46
Rozdzia 1: C# 2.0
}
public int CompareTo(Node<T> rhs)
{
return data.CompareTo(rhs.data);
}
public bool Equals(Node<T> rhs)
{
return this.data.Equals(rhs.data);
}
// metody
public Node<T> Add(Node<T> newNode)
{
if (this.CompareTo(newNode) > 0) // wstawienie przed wze
// biecy
{
newNode.next = this; // wskanik next ustawiany na wze
// biecy jeli istnieje wze poprzedni, powinien od tego
// momentu wskazywa polem next nowy wze
if (this.prev != null)
{
this.prev.next = newNode;
newNode.prev = this.prev;
}
// wskanik poprzednika wza biecego ma wskazywa nowy
// wze
this.prev = newNode;
// zwrcenie referencji nowego wza, jeli sta si nowym
// czoem listy
return newNode;
}
else
{
47
return this;
}
}
public override string ToString()
{
string output = data.ToString();
if (next != null)
{
output += ", " + next.ToString();
}
return output;
}
// koniec klasy
// waciwoci
// indekser
48
Rozdzia 1: C# 2.0
node.Next;
++ctr;
} // koniec while
throw new ArgumentOutOfRangeException();
} // koniec get
// koniec indeksera
// konstruktor
public LinkedList()
{
}
// metody
public void Add(T data)
{
if (headNode == null)
{
headNode = new Node<T>(data);
}
else
{
headNode = headNode.Add(new Node<T>(data));
}
}
public override string ToString()
{
if (this.headNode != null)
{
return this.headNode.ToString();
}
else
{
return string.Empty;
}
}
49
class Program
{
private static void DisplayList<T>(string intro, LinkedList<T>
theList) where T : IComparable<T>
{
Console.WriteLine(intro + ": " + theList);
}
// punkt wejcia
static void Main(string[] args)
{
LinkedList<Pilgrim> pilgrims = new LinkedList<Pilgrim>();
pilgrims.Add(new Pilgrim("Rycerz"));
pilgrims.Add(new Pilgrim("Mynarz"));
pilgrims.Add(new Pilgrim("Szeryf"));
pilgrims.Add(new Pilgrim("Kucharz"));
pilgrims.Add(new Pilgrim("Adwokat"));
DisplayList<Pilgrim>("Pilgrims", pilgrims);
Console.WriteLine("Przegldanie listy pielgrzymw...");
// teraz lista daje si przeglda, wic mona j
// zastosowa w ptli foreach
foreach (Pilgrim p in pilgrims)
{
Console.WriteLine("Zawd pielgrzyma to " + p.ToString());
}
}
}
}
50
Rozdzia 1: C# 2.0
Wynik:
Pielgrzymi: Adwokat, Kucharz, Mynarz, Rycerz, Szeryf
Przegldanie listy pielgrzymw...
Zawd pielgrzyma to Adwokat
Zawd pielgrzyma to Kucharz
Zawd pielgrzyma to Mynarz
Zawd pielgrzyma to Rycerz
Zawd pielgrzyma to Szeryf
Jak to dziaa?
Lista implementuje teraz enumerator; implementacja polega na zainicjowaniu ptli foreach dla czoowego wza listy (klasa wza rwnie implementuje interfejs IEnumerable). Implementacja zwraca obiekt danych
zwrcony przez wze:
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
foreach (Node<T> node in this.headNode)
{
yield return node.Data;
}
}
W miejsce instrukcji
yield kompilator
automatycznie
generuje
zagniedon
implementacj
IEnumerator.
Zapamituje tam
stan iteracji;
programista musi
jedynie wskaza
wartoci
do zwrcenia
w kolejnych krokach
iteracji.
51
A co
znaczy wystpujce w implementacji LinkedList danie przejrzenia
(foreach) elementw Node<T> w headNode? Przecie headNode to nie lista,
a jeden z jej wzw (konkretnie wze czoowy)?
Ot headNode to faktycznie czoowy wze listy. Poniewa jednak klasa
Node implementuje interfejs IEnumerable, dla potrzeb iteracji wze zachowuje si jak kolekcja. Cho brzmi to niedorzecznie, jest cakiem uzasadnione, bo wze w istocie przejawia pewne cechy kolekcji, w tym przynajmniej sensie, e potrafi wskaza nastpny element kolekcji (nastpny
wze listy). Cao mona by przeprojektowa tak, eby wzy nie byy
tak sprytne, za to sama lista bya sprytniejsza wtedy zadanie
realizacji iteracji spoczywaoby w caoci na licie i ta nie delegowaaby
zadania do wzw.
Wicej informacji
O interfejsie IEnumerable<T> mona si sporo dowiedzie z plikw pomocy
MSDN dla hasa Topic: IEnumerable<T>.
Jak to zrobi?
Zastosowania metod anonimowych najlepiej zilustrowa przykadem:
1.
52
Rozdzia 1: C# 2.0
2.
3.
4.
Metody anonimowe
pozwalaj na
stosowanie blokw
kodu w roli
parametrw.
53
A co
z innymi zastosowaniami metod anonimowych? Czy mona je stosowa we wasnym kodzie?
aden problem. Metody anonimowe mona stosowa nie tylko przy inicjalizowaniu delegacji, ale i wszdzie tam, gdzie dozwolone jest uycie delegacji we wszystkich tych miejscach mona przekaza nienazwany
blok kodu.
jeli w takim bloku kodu nastpi odwoanie do zmiennej lokalnej?
Dobre pytanie. To do mylca sytuacja i atwo tu o pomyk, zwaszcza
kiedy nie jest si w peni wiadomym konsekwencji takich odwoa. Ot
C# pozwala na wciganie zmiennych lokalnych do zasigu anonimowego
bloku kodu; odwoania do nich s wykonywane w momencie wykonania
owego bloku kodu. Moe to prowokowa rozmaite efekty uboczne choby
podtrzymywanie przy yciu obiektw, ktrymi inaczej ju dawno zaopiekowaby si mechanizm zbierania nieuytkw.
54
Rozdzia 1: C# 2.0
Wicej informacji
W zasobach MSDN mona znale wietny artyku traktujcy o metodach
anonimowych. Mowa o artykule Create Elegant Code with Anonymous
Methods, Iterators and Partial Classes autorstwa Juvala Lowyego. Warto
te zapozna si z artykuem z serwisu ONDotnet.com (OReilly), publikowanym pod adresem http://www.ondotnet.com/pub/a/dotnet/2004/04/05/
csharpwhidbeypt1.html.
Sowo kluczowe
partial pozwala
na podzia definicji
klasy na wiele plikw.
55
Jak to zrobi?
Praktyczne zastosowanie typw czciowych mona zilustrowa na bazie
poprzedniego przykadu (AnonymousMethods). Spjrzmy na deklaracj klasy
w pliku Form1.cs:
partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.button1.Click += delegate { label1.Text = "Do widzenia!"; };
}
//
//
//
//
}
56
Rozdzia 1: C# 2.0
Kompletn definicj klasy Form1 daj dopiero te dwa pliki wzite razem;
podzia klasy pozwala na wyodrbnienie kodu tworzonego przez programist i kodu generowanego automatycznie przez rne mechanizmy rodowiska programistycznego. Czyni to projekt przejrzystszym i prostszym.
Stosujc klasy czciowe, trzeba mie wiadomo kilku aspektw:
Wszystkie czciowe definicje typw musz zawiera sowo kluczowe
partial i musz nalee do tej samej przestrzeni nazw oraz tego samego moduu i podzespou.
Modyfikator partial moe wystpowa jedynie przed sowami kluczowymi class, inerface i struct.
We wszystkich definicjach czciowych naley uzgodni modyfikatory dostpu do skadowych (public, private itd.).
A co
ze stosowaniem klas czciowych we wasnych projektach?
Microsoft sugeruje, e klasy czciowe mog przyda si programistom
pracujcym w zespoach mog wtedy podzieli si prac nad klasami.
Wci jednak za wczenie, aby stwierdzi, czy taka praktyka si przyjmie; osobicie uwaam, e kada klasa tak rozbudowana, aby jej rozmiar uzasadnia podzia pracy, powinna po prostu zosta podzielona na
mniejsze klasy. Na razie wic gwnym zastosowaniem typw czciowych jest upychanie po ktach rozmaitych tworw generowanych przez
kreatory rodowiska programistycznego Visual Studio 2005.
Wicej informacji
Dobry artyku o typach czciowych mona znale w archiwach witryny
Developer.com. Publikowany jest pod adresem http://www.developer.com/
net/net/article.php/2232061.
57
Jak to zrobi?
Utworzenie klasy statycznej polega na poprzedzeniu nazwy klasy sowem
kluczowym static i upewnieniu si, e sama definicja klasy spenia podane
wyej kryterium co do metod. Trzeba te pamita o dodatkowych ograniczeniach nakadanych na klasy statyczne:
Klasy statyczne mog zawiera wycznie statyczne skadowe.
Nie wolno tworzy egzemplarza klasy statycznej.
Wszystkie klasy statyczne s klasami finalnymi (bezpodnymi) nie
mona wyprowadza z nich klas pochodnych.
Oprcz tego klasa statyczna nie moe zawiera konstruktora. Waciwe
zastosowanie klasy statycznej ilustruje kod z listingu 1.6.
Listing 1.6. Stosowanie klas statycznych
#region Using directives
using System;
#endregion
namespace StaticClass
{
public static class CupConversions
{
public static int CupToOz(int cups)
{
return cups * 8; // szklanka to 8 uncji pynu
}
public static double CupToPint(double cups)
{
return cups * 0.5; // szklanka to p pinty
58
Rozdzia 1: C# 2.0
}
public static double CupToMil(double cups)
{
return cups * 237; // 237 mililitrw to jedna szklanka
}
public static double CupToPeck(double cups)
{
return cups / 32; // 8 kwart to 1 peck
}
public static double CupToBushel(double cups)
{
return cups / 128; // 4 pecki to 1 buszel
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Nie kady wie, e " +
"szklanka pynu da si przeliczy na: ");
Console.WriteLine(CupConversions.CupToOz(1) + " uncji");
Console.WriteLine(CupConversions.CupToPint(1) + " pint");
Console.WriteLine(CupConversions.CupToMil(1) + " mililitrw");
Console.WriteLine(CupConversions.CupToPeck(1) + " peckw");
Console.WriteLine(CupConversions.CupToBushel(1) + " buszli");
}
}
}
Wynik:
Nie kady wie, e szklanka pynu da si przeliczy na:
8 uncji
0.5 pint
237 mililitrw
0.03125 peckw
0.0078125 buszli
Gwna metoda klasy Program wywouje statyczne metody klasy CupConversions. Poniewa klasa ta istnieje tylko jako zasobnik metod narzdziowych
(pomocniczych), a obiekt klasy nie jest wcale potrzebny, klasa CupConversion moga zosta zdefiniowana jako statyczna.
A co
z polami i waciwociami? Czy klasa statyczna moe mie takie
skadowe?
Tworzenie klas statycznych
59
Owszem, moe, ale wszelkie skadowe (metody, pola i waciwoci) powinny by rwnie statyczne.
Wicej informacji
Klasy statyczne zostay omwione midzy innymi w znakomitym artykule
Erica Gunnersona. Artyku jest dostpny w zasobach MSDN pod adresem
http://blogs.msdn.com/ericgu/archive/2004/04/13/112274.aspx.
Wyraanie wartoci
pustych typami nullable
Typy nullable
pozwalaj na
wyrnienie wartoci
pustych rwnie
w takich typach
prostych, jak typy
cakowitoliczbowe
czy typy logiczne.
Nowe typy, tzw. typy nullable, to takie typy proste, ktrym mona przypisywa wartoci puste i wartoci te da si potem odrni od wartoci
z waciwej dziedziny typu. Moliwo ta okazuje si niezwykle uyteczna,
zwaszcza przy pracy z bazami danych, kiedy to zwracana warto pola
moe by wartoci pust; bez moliwoci przepisania takiego pola do
typu nullable nie mona by stwierdzi, czy pole reprezentuje warto pust,
czy moe zero (a to rnica!); nie mona by te wyrazi wartoci logicznej, ktra nie reprezentuje jeszcze ani prawdy, ani faszu.
Jak to zrobi?
Typ mogcy przyjmowa wartoci puste deklaruje si nastpujco:
System.Nullable<T> zmienna
Warto pust zmiennej typu nullable wykrywa si dwojako. Mona zastosowa konstrukcj tak:
if (myNullableInt.HasValue)
60
Rozdzia 1: C# 2.0
albo tak:
if (myNullableInt != null)
61
if ( myNullableBool != null )
{
Console.WriteLine("myNullableBool: " + myNullableBool);
}
else
{
Console.WriteLine("myNullableBool ma warto pust!");
}
myNullableInt = null;
// sprowokuje wyjtek,
// kiedy x bdzie puste
Console.WriteLine("b: " + b);
}
catch (System.Exception e)
{
Console.WriteLine("Wyjtek! " + e.Message);
}
int c = myNullableInt ?? -1; // przypisze 1, kiedy x bdzie
// puste
Console.WriteLine("c: {0}", c);
// ostronie z zaoeniami jeli ktrykolwiek z operandw
// bdzie pusty, wszelkie porwnania dadz wynik false!
if (myNullableInt >= c)
{
Console.WriteLine("myNullableInt jest wiksze (rwne) od c");
}
else
{
Console.WriteLine("Czy myNullableInt jest mniejsze od c?");
}
}
}
}
Wynik:
myNullableInt to 25
myNullableDouble: 3.14159
myNullableBool ma warto pust!
62
Rozdzia 1: C# 2.0
Jak to dziaa?
Skupmy si na metodzie Main. Nastpuje tu utworzenie piciu zmiennych
typw prostych, z wyrnieniem wartoci pustych:
int? myNullableInt = 25;
double? myNullableDouble = 3.14159;
bool? myNullableBool = null; // ani tak, ani nie
// string? myNullableString = "Ahoj";
// Dog? myNullableDog = new Dog(3);
Wyjtkiem s
struktury, ktre
cho s typami
definiowanymi przez
uytkownika
mog by
wykorzystywane
jako typy nullable.
Jeli ktrykolwiek
z operandw
ma warto pust,
operatory relacji
dadz w wyniku
false!
63
O S TR Z E ENIE
Wyjtkiem jest operator ==, ktry da warto
gdy oba operandy bd puste.
true
rwnie wtedy,
Klauzula else moe zosta uruchomiona, kiedy okae si, e albo myNullableInt ma warto mniejsz od c, albo myNullableInt bd c jest
puste.
A co
z pustymi wartociami logicznymi? Jak wypadaj ich porwnania i jak
je odnie do trjwartociowych typw logicznych charakterystycznych
dla SQL?
64
Rozdzia 1: C# 2.0
x&y
x|y
prawda
prawda
prawda
prawda
prawda
fasz
fasz
prawda
prawda
pusta
pusta
prawda
fasz
prawda
fasz
prawda
fasz
fasz
fasz
fasz
fasz
pusta
fasz
pusta
pusta
prawda
pusta
prawda
pusta
fasz
fasz
pusta
pusta
pusta
pusta
pusta
Wicej informacji
wietny artyku o typach nullable mona znale w czeluciach Visual
C# Developer Center (http://msdn.microsoft.com/vcsharp/2005/overview/
language/nullabletypes/).
Kwalifikator
globalnej przestrzeni
nazw pozwala
na odwoywanie si
do identyfikatora
z (domylnie)
globalnej przestrzeni
nazw; normalnie
odwoania
s ograniczane
do zestawu
identyfikatorw
z lokalnej przestrzeni
nazw,
a identyfikatory
z tej przestrzeni
przesaniaj nazwy
definiowane
globalnie.
65
Odwoania do obiektw
z globalnej przestrzeni nazw
Tak jak w poprzednim wydaniu jzyka C# do deklarowania zasigu widocznoci nazw (tzw. przestrzeni nazw) suy sowo kluczowe namespace.
Stosowanie przestrzeni nazw pozwala lepiej organizowa kod i zapobiega
ewentualnym kolizjom nazw (na przykad prbie zdefiniowania dwch klas
o identycznej nazwie) przydatno podziau przestrzeni nazw ujawnia
si zwaszcza przy korzystaniu z komponentw zewntrznych, tworzonych
przez osoby trzecie.
nazw s dostpne dla obiektw wszystkich pozostaych (wszych) przestrzeni nazw. W przypadku kolizji nazw potrzebny jest jednak sposb sygnalizowania, e dane odwoanie dotyczy nie lokalnej, a wanie globalnej przestrzeni nazw.
Jak to zrobi?
Odwoanie do obiektu z globalnej przestrzeni nazw naley zasygnalizowa
kwalifikatorem zasigu global::, jak na listingu 1.8.
Listing 1.8. Stosowanie globalnej przestrzeni nazw
using System;
namespace GlobalNameSpace
{
class Program
{
// utworzenie zagniedonej klasy System udostpniajcej zestaw
// narzdzi interakcji z obsugiwanym przez program systemem;
// nazwa System koliduje z nazw przestrzeni nazw System
public class System
{
}
static void Main(string[] args)
{
// znacznik sygnalizujcy uruchomienie aplikacji konsoli;
// koliduje z nazw Console z przestrzeni nazw System
bool Console = true;
int x = 5;
// Console.WriteLine(x); // odmowa kompilacji kolizja
// z lokalnym Console
// System.Console.WriteLine(x); // kolizja z lokalnym System
global::System.Console.WriteLine(x); // dziaa
global::System.Console.WriteLine(Console);
}
}
}
Wynik:
5
True
66
Rozdzia 1: C# 2.0
Jak to dziaa?
Tworzenie zagniedonej w klasie Program klasy o nazwie System i deklarowanie w metodzie Main zmiennej lokalnej o nazwie Console to przykad cokolwiek sztuczny. Tym niemniej takie deklaracje blokuj w lokalnej
przestrzeni nazw dostp do globalnych identyfikatorw System i Console,
co uniemoliwia kompilacj wywoa:
Console.WriteLine(x);
System.Console.WriteLine(x);
Warto te zauway, e w ostatnim wierszu kodu w odwoaniu do globalnych identyfikatorw System i Console stosowany jest kwalifikator globalnej przestrzeni nazw, a niekwalifikowane odwoanie do Console dotyczy lokalnej zmiennej metody:
global::System.Console.WriteLine(Console);
A co
z innymi zastosowaniami operatora zasigu (::)?
Operator :: suy jako kwalifikator aliasu przestrzeni nazw. Wystpuje
zawsze pomidzy dwoma identyfikatorami:
identyfikator1::identyfikator2
Jeli identyfikator1 reprezentuje globaln przestrze nazw, operator zasigu suy do wycignicia identyfikatora identyfikator2 z teje przestrzeni globalnej. Ale jeli identyfikator1 bdzie dowoln przestrzeni
nazw inn od przestrzeni globalnej, operator zawzi poszukiwania identyfikator2 do zasigu identyfikator1.
Wicej informacji
Kwalifikator globalnej przestrzeni nazw wspominany jest w artykule Create Elegant Code with Anonymous Methods, Iterators and Partial Classes
67
Nowa specyfikacja jzyka C# pozwala na ograniczanie poziomu dostpnoci metod-akcesorw ustawiajcych i odczytujcych waciwoci. Su
do tego stosowne modyfikatory dostpu. Zwykle dostp ogranicza si jedynie do akcesorw ustawiajcych waciwoci (set); akcesory odczytujce
s zazwyczaj udostpniane publicznie.
Jak to zrobi?
Ograniczenie dostpu do akcesora waciwoci polega na opatrzeniu deklaracji tego akcesora stosownym modyfikatorem dostpu, jak na listingu 1.9.
Listing 1.9. Ograniczanie dostpu do akcesora waciwoci
#region Using directives
using System;
using System.Collections.Generic;
using System.Text;
#endregion
namespace LimitPropertyAccess
{
public class Employee
{
private string name;
public Employee(string name)
{
this.name = name;
}
public string Name
{
get { return name; }
protected set { name = value; }
}
public virtual void ChangeName(string name)
{
// tu operacje aktualizujce rekordy
Name = name; // odwoanie do prywatnego akcesora
}
}
class Program
68
Rozdzia 1: C# 2.0
{
static void Main(string[] args)
{
Employee joe = new Employee("Joe");
// inne operacje
string whatName = joe.Name; // dziaa
// joe.Name = "Bob"; // odmowa kompilacji
joe.ChangeName("Bob"); // dziaa
Console.WriteLine("imi joe'a: {0}", joe.Name);
}
}
}
Wynik:
imi joe'a: Bob
Jak to dziaa?
Projekt klasy Employee (pracownik) sygnalizuje, e cig imienia pracownika ma by prywatny. Ale programista przewidzia, e kiedy przyjdzie
mu wstawia dane o pracownikach do bazy danych, wic udostpni imi
za porednictwem waciwoci Name.
Pozostae klasy programu powinny mie moliwo odwoywania si do
Name, ale jedynie w odwoaniach niemodyfikujcych. Zmiana wartoci pola
moe si odbywa jedynie za porednictwem jawnego wywoania metody
ChangeName. Metoda zostaa oznaczona jako wirtualna w przyszych
klasach pochodnych zmiana imienia pracownika bdzie si pewnie wizaa z dodatkowymi operacjami.
Zachodzi tu potrzeba udostpnienia akcesora set, ale tylko metodom klasy
Employee i metodom jej klas pochodnych. Ograniczenie takie mona wy-
A co
z ograniczeniami odnonie stosowania modyfikatorw dostpu?
Ot modyfikatorw tych nie mona stosowa wobec interfejsw i jawnych
implementacji skadowych interfejsw. Modyfikatory dostpu mona stosowa jedynie wtedy, kiedy waciwo obejmuje oba akcesory (get i set);
mona nimi przy tym opatrywa tylko jeden z akcesorw.
Ograniczanie dostpu do waciwoci
69
Wicej informacji
O waciwociach i modyfikatorach dostpu do waciwoci traktuje artyku
MSDN publikowany pod adresem http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfPropertiesPG.asp.
Nowa specyfikacja jzyka C# zezwala na okrelanie metod delegacji z typem wartoci zwracanej, bdcej pochodn (bezporedni bd poredni)
typu zwracanego okrelonego w definicji delegacji; operacja taka nosi miano kowariancji. Chodzi o to, e jeli definicja delegacji zakada zwracanie
wartoci typu Mammal (ssak), to delegacj t mona zastosowa do metody
zwracajcej warto typu Dog (pies), o ile Dog jest pochodn Mammal, a take
do metody zwracajcej warto typu Retriever, o ile Retriever to pochodna
typu Dog, a ten jest pochodn Mammal.
Analogicznie dozwolone jest przekazywanie sygnatury metody delegacji,
w ktrej typy parametrw s pochodnymi typw parametrw zdefiniowanych w delegacji. To z kolei okrela si mianem kontrawariancji. Chodzi
o to, e jeli definicja delegacji wymaga podania metody przyjmujcej parametr typu Dog, to mona j uy z metod przyjmujc parametr typu
Mammal (znw pod warunkiem e Dog to pochodna Mammal).
Kontrawariancja
pozwala
na stosowanie
z delegacjami metod
przyjmujcych
parametry typu
bdcego typem
bazowym
(bezporednim
lub porednim)
typu parametru
okrelonego
w definicji delegacji.
70
Rozdzia 1: C# 2.0
Jak to zrobi?
71
Wynik:
Ssak
Pies
W metodzie akceptujcej psy
W metodzie akceptujcej ssaki
Jak to dziaa?
Klasa Program z listingu 1.10 deklaruje dwie delegacje. Pierwsza z nich
obejmuje metody bezparametrowe i zwracajce wartoci typu Mammal:
Z wczeniejszych
definicji z listingu 1.10
wynika, e Dog
to pochodna
Mammal.
W metodzie run deklarowane s egzemplarze klas Mammal i Dog (po jednym z kadej klasy):
Mammal m = new Mammal();
Dog d = new Dog();
72
Rozdzia 1: C# 2.0
Pierwszy egzemplarz delegacji jest konstruowany na bazie metody pasujcej do sygnatury deklaracji: metoda zwraca typ void i deklaruje parametr typu Dog:
theContraVariantDelegate myContraVariantDelegate =
new theContraVariantDelegate(MyMethodThatTakesADog);
Zauwa,
e kontrawariancja
pozwala
na przekazywanie
obiektu klasy
bazowej tam,
gdzie oczekiwany
jest obiekt klasy
pochodnej.
73
A co
z t kontrawariancj? Wiadomo, e dziki kowariancji da si zwrci
obiekt typu Dog (bo Mammal zawiera si w Dog), ale skd moliwo i jaki
sens podstawienia odwrotnego? Czy tam, gdzie oczekiwany jest pewien
typ, nie powinno si przekazywa tego wanie typu, ewentualnie typu
pochodnego?
Dr Jonathan Bruce
Postel (1913 1998),
wsptwrca
standardw
internetowych.
Wicej informacji
Dodatkowych informacji naleaoby szuka w archiwach licznych grup
dyskusyjnych, gdzie toczyy si zaarte spory o zalety i wady jzykw
obiektowych obsugujcych kowariancj i kontrawariancj. Szczegowego
omwienie stosowania kowariancji i kontrawariancji w programach pisanych w jzyku C# naley szuka w dokumentacji MSDN.
74
Rozdzia 1: C# 2.0