Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

Waldemar Bartyna 1 Programowanie zaawansowane Interfejsy.

Podobne prezentacje


Prezentacja na temat: "Waldemar Bartyna 1 Programowanie zaawansowane Interfejsy."— Zapis prezentacji:

1 Waldemar Bartyna 1 Programowanie zaawansowane Interfejsy

2 Waldemar Bartyna Bez dziedziczenia 2

3 Waldemar Bartyna Dziedziczenie 3

4 Waldemar Bartyna Dziedziczenie 4

5 Waldemar Bartyna Bez polimorfizmu 5

6 Waldemar Bartyna Bez polimorfizmu 6

7 Waldemar Bartyna Informacje dla polimorfizmu 7

8 Waldemar Bartyna Polimorfizm 8

9 Waldemar Bartyna Interfejsy 1.Definicja interfejsu 2.Implementacja standardowych interfejsów:  Enumeracja,  Klonowanie,  Porównywanie. 3.Wykorzystanie interfejsów jako mechanizmu wywołań zwrotnych 9

10 Waldemar Bartyna 10 Definicja interfejsu

11 Waldemar Bartyna Definicja interfejsu Interfejs jest nazwanym zbiorem abstrakcyjnych składowych. Dobór składowych definiowanych przez interfejs zależy od zachowania, które dany interfejs modeluje. Interfejs wyraża pewne zachowanie, które wybrana klasa lub struktura musi zaimplementować poprzez implementację wszystkich abstrakcyjnych składowych tego interfejsu. Klasa (lub struktura) może implementować wiele interfejsów, tym samym wspierając wiele zachowań. 11

12 Waldemar Bartyna Przykładowy interfejs (IDbConnection) Biblioteka klas bazowych platformy.NET zawiera setki predefiniowanych interfejsów, implementowanych przez różne klasy i struktury. Na przykład, ADO.NET posiada definicje wielu dostawców danych, którzy umożliwiają połączenie się z różnymi systemami zarządzania bazami danych (SqlConnection, OracleConnection, OdbcConnection). Pomimo tego, że każdy z tych obiektów połączeniowych ma własną unikalną nazwę, należy do innej przestrzeni nazw, jak również może być częścią różnych pakietów, wszystkie te obiekty implementują wspólny interfejs: IDbConnection. 12

13 Waldemar Bartyna Przykładowy interfejs (IDbConnection) c. d. 13

14 Waldemar Bartyna Przykładowy interfejs (IDbConnection) c. d. 2 Dzięki temu, że każdy z obiektów połączeniowych implementuje interfejs IDbConnection (wspiera takie zachowanie), mamy gwarancję, że każdy z tych obiektów posiada metody pozwalające na otwieranie i zamykania połączenia (Open(), Close()), tworzenie poleceń (CreateCommand()) itd.. Interfejs ten (jak również jego składowe), dostawcy danych i obiekty połączeniowe będą dokładnie omówione na wykładzie poświęconym ADO.NET. 14

15 Waldemar Bartyna Kilka zasad Konwencja przyjęta w.NET wymaga, aby nazwy interfejsów poprzedzane były dużą literą I (i jak interfejs). Wszystkie składowe interfejsu są zawsze abstrakcyjne, co oznacza, że 15 1.Każda z klas implementujących dany interfejs, musi te wszystkie składowe zaimplementować. 2.Każda z klas wspierających dany interfejs, może te składowe zaimplementować w dowolny, odpowiedni dla jej specyfiki działania, sposób.

16 Waldemar Bartyna Przykładowy interfejs (IDropTarget) W przestrzeni nazw System.Windows.Forms zdefiniowana jest klasa Control, będącą klasą bazową dla wielu elementów interfejsu graficznego w projektach Windows Forms takich jak: DataGridView, Label, StatusBar, itd.. Klasa Control implementuje interfejs IDropTarget, który definiuje podstawową funkcjonalność typu przeciągnij i upuść ( ang. drag-and-drop). 16

17 Waldemar Bartyna Przykładowy interfejs (IDropTarget) c. d. Znając ten interfejs i wiedząc, że implementuje go klasa System.Windows.Forms.Control, możemy słusznie zakładać, że każda klasa, która rozszerza tą klasę (po niej dziedziczy) posiada metody OnDragDrop(), OnDragEnter(), OnDragLeave(), OnDragOver(). 17

18 Waldemar Bartyna Interfejs a klasa abstrakcyjna Klasa oznaczona jako abstrakcyjna może definiować dowolną liczbę abstrakcyjnych składowych, tworząc w ten sposób interfejs polimorficzny dla klas pochodnych. W przypadku, gdy klasa definiuje zbiór abstrakcyjnych składowych, może również zdefiniować dowolną liczbę konstruktorów, pól danych, nieabstrakcyjnych składowych (posiadających implementację), itd. Interfejsy, z drugiej strony, zawierają tylko abstrakcyjne składowe. 18

19 Waldemar Bartyna Ograniczenia interfejsu polimorficznego Tylko klasy pochodne musza wspierać składowe zdefiniowane przez abstrakcyjną klasę bazową. W dużych systemach, nie jest niczym nadzwyczajnym projektowanie wielu hierarchii klas, które nie mają wspólnej klasy nadrzędnej (nie licząc oczywiście klasy System.Object). W oparciu tylko o mechanizm klas abstrakcyjnych, nie ma sposobu na skonfigurowanie typów w różnych hierarchiach tak, aby wspierały ten sam interfejs polimorficzny. 19

20 Waldemar Bartyna Prosty przykład Tylko typy pochodne klasy ClonableType muszą implementować metodę Clone(). Inne klasy, niedziedziczące po klasie ClonableType, nie są objęte jej interfejsem polimorficznym. 20

21 Waldemar Bartyna Rozwiązaniem są interfejsy Interfejs, po zdefiniowaniu, może być implementowany przez: dowolną klasę lub strukturę, w dowolnej hierarchii, w dowolnej przestrzeni nazw, w dowolnym pakiecie, definiowany w dowolnym jeżyku programowania platformy.NET. Interfejsy są wysoce polimorficzne 21

22 Waldemar Bartyna Przykład możliwości interfejsu Przykładem interfejsu może być interfejs IClonable zdefiniowany w przestrzeni nazw System. Interfejs ten definiuje jedną metodę o nazwie Clone(). W.NET Framework 3.5, wiele typów, pozornie w żaden sposób nie powiązanych (System.Array, System.Data.SqlClient.SqlConnection, System.OperatingSystem, System.String), implementuje właśnie interfejs IClonable. 22

23 Waldemar Bartyna Przykład możliwości interfejsu c. d. Pomimo, że wymienione poprzednio klasy nie mają wspólnej klasy nadrzędnej (oprócz System.Object), mogą one brać udział w polimorfizmie za pośrednictwem interfejsu IClonable. Mając metodę, która posiada parametr typu IClonable, moglibyśmy do niej przekazać dowolny obiekt implementujący ten interfejs. 23

24 Waldemar Bartyna Przykład możliwości interfejsu c. d. 2 Po uruchomieniu programu zobaczymy wypisane nazwy wszystkich obiektów, pobrane za pomocą dziedziczonej po klasie System.Object metody GetType(). 24

25 Waldemar Bartyna Drugi problem z klasami abstrakcyjnymi Każdy z typów pochodnych musi dostarczyć implementację wszystkich abstrakcyjnych składowych klasy bazowej. Dla wyjaśnienia załóżmy, że do naszej abstrakcyjnej klasy Shape dodaliśmy metodę abstrakcyjną GetNumberOfPoints(), która zwraca liczbę wierzchołków. 25

26 Waldemar Bartyna Ponownie, rozwiązaniem są interfejsy Jedyną klasą, która ma wierzchołki jest klasa Hexagon. Mimo, że w przypadku pozostałych klas, pobieranie liczby wierzchołków nie ma sensu, wszystkie klasy pochodne muszą być zgodne z interfejsem polimorficznym klasy bazowej. Rozwiązaniem problemu jest zdefiniowanie interfejsu wyrażającego zachowanie „posiadania wierzchołków”. Taki interfejs może być implementowany przez odpowiednie klasy, bez jakichkolwiek zmian w pozostałych klasach. 26

27 Waldemar Bartyna Definiowanie nowego interfejsu Na poziomie syntaktycznym, interfejs definiujemy poprzez słowo kluczowe interface. W odróżnieniu od pozostałych typów w.NET, interfejsy nie posiadają klas bazowych (nawet klasy bazowej System.Object). Przy składowych interfejsu nie specyfikujemy modyfikatorów dostępu. Wszystkie składowe interfejsów są domyślnie publiczne i abstrakcyjne. 27

28 Waldemar Bartyna Definiowanie nowego interfejsu c. d. Ponieważ wszystkie składowe interfejsu są abstrakcyjne, nie posiadają żadnej implementacji. Taką deklarację metody nazywamy jej prototypem. Za implementowanie składowych interfejsu odpowiedzialne są klasy i struktury wspierające dany interfejs. 28

29 Waldemar Bartyna Przykład źle zdefiniowanego interfejsu 29

30 Waldemar Bartyna Składowe interfejsu Interfejsy mogą definiować prototypy metod, właściwości, zdarzeń i indeksatorów. Dla przykładu, nasz interfejs możemy zdefiniować za pomocą właściwości tylko do odczytu, zamiast tradycyjnej metody. 30

31 Waldemar Bartyna Użyteczność interfejsu Interfejsy, same w sobie, są bezużyteczne; są przecież tylko nazwanym zbiorem abstrakcyjnych składowych. Nie nożna stworzyć obiektu interfejsu. Znaczenie i użyteczność interfejsów rozpoczyna się od momentu ich zaimplementowania przez wybrane klasy i struktury. W naszym przykładzie, niektóre klasy (posiadające wierzchołki) będą implementować interfejs IPointy, pozostałe (takie jak Circle) nie będą tego robić. 31

32 Waldemar Bartyna Implementowanie interfejsów Listę implementowanych interfejsów podajmy po przecinkach w definicji typu Klasa bazowa musi być pierwsza pozycją po dwukropku. Gdy klasa dziedziczy po System.Object, po dwukropku podajemy od razu listę interfejsów. W przypadku struktur (które domyślnie dziedziczą po klasie System.ValueType), listę implementowanych interfejsów umieszczamy zaraz po definicji struktury. 32

33 Waldemar Bartyna Implementowanie interfejsów c. d. 33

34 Waldemar Bartyna Wszystko albo nic Implementowanie interfejsu rządzie się prostą regułą wszystko albo nic. Typ implementujący wybrany interfejs musi zaimplementować każdą ze zdefiniowanych w tym interfejsie składowych. I tak, w przypadku wspomnianego wcześniej interfejsu IDbConnection, wspierające go klasy muszą zaimplementować dziesięć jego składowych. 34

35 Waldemar Bartyna Klasa Triangle 35

36 Waldemar Bartyna Nowa wersja klasy Hexagon 36

37 Waldemar Bartyna Zmieniona hierarchia klas 37

38 Waldemar Bartyna Wywołanie składowych interfejsu Najprostszym sposobem skorzystania z nowej funkcjonalności klasy wspierającej dany interfejs jest bezpośrednie wywołanie odpowiedniej metody. Taki sposób sprawdza się oczywiście w przypadku, gdy wiemy, jakie interfejsy wspiera dana klasa. Co zrobić w sytuacji, gdy tej wiedzy nie posiadamy? (Mamy np. do czynienia z tablicą obiektów typu Shape.) Przeanalizujemy trzy możliwe rozwiązania. 38

39 Waldemar Bartyna Użycie odpowiedniego bloku try/catch Jeżeli dana klasa nie wspiera interfejsu, zostanie zgłoszony wyjątek InvalidCastException. 39

40 Waldemar Bartyna Użycie słowa kluczowego as Jeżeli dany obiekt wspiera interfejs IPointy, zostanie zwrócona referencja do tego interfejsu. W przeciwnym wypadku zostanie zwrócony null. 40

41 Waldemar Bartyna Użycie słowa kluczowego is Jeżeli dany obiekt wspiera interfejs IPointy, zostanie zwrócona wartość true, w przeciwnym razie wartość false. 41

42 Waldemar Bartyna Interfejsy jako parametry Ponieważ interfejsy to jedne z typów platformy.NET, możemy definiować metody, które przyjmuję interfejsy jako parametry. Dla przykładu, zdefiniujmy jeszcze jeden interfejs, który będzie modelował możliwość rysowanie obiektów danej klasy w trój-wymiarze. 42

43 Waldemar Bartyna Interfejsy jako parametry c. d. Interfejs ten będzie wspierany przez klasy Circle i Hexagon. 43

44 Waldemar Bartyna Interfejsy jako parametry c. d. 2 Definiujemy metodę, która rysuje obiekty wspierające interfejs IDraw3D. Przekazanie parametru nie wspierającego tego interfejsu spowoduje błąd kompilatora. 44

45 Waldemar Bartyna Interfejsy jako wartości zwracane Interfejsy mogą być również wykorzystane jako typ wartości zwracanej przez metodę. Dla przykładu, zdefiniujmy metodę, która bierze dowolny obiekt System.Object, sprawdza jego kompatybilność z interfejsem IPointy i zwraca referencję do uzyskanego interfejsu (jeżeli jest on wspierany). 45

46 Waldemar Bartyna Interfejsy jako wartości zwracane c. d. Zdefiniowaną metodę można przetestować w następujący sposób: 46

47 Waldemar Bartyna Tablice interfejsów Ten sam interfejs może być implementowany przez wiele typów, nie należących nawet do tych samych hierarchii klas (nie mających wspólnej klasy bazowej, poza klasą System.Object). Fakt ten daje możliwość tworzenie wielu zaawansowanych konstrukcji programistycznych. Załóżmy, że zdefiniowaliśmy dodatkowo trzy klasy wspierające interfejs IPointy, niepołączone żadną hierarchią klas. 47

48 Waldemar Bartyna Tablice interfejsów c. d. 48 Możemy teraz zdefiniować tablicę „spiczastych” obiektów (implementujących interfejs IPointy. Wszystkie elementy takie j tablicy mogą być traktowane jako kompatybilne z typem IPointy, pomimo ich dużej różnorodności i przynależności do różnych hierarchii klas.

49 Waldemar Bartyna Korzystajmy z pomocy środowiska! Implementacja interfejsu wymaga sporo pisania. W końcu trzeba zaimplementować każdą składową interfejsu w każdym wspierającym ją typie. Środowisko Visual Studio (jak również Express Edition) ułatwia nam to zadanie poprzez „sprytne znaczniki”, które pojawiają się po najechaniu kursorem na nazwę interfejsu. Po wybraniu opcji „Implement interfece nazwa”, środowisko wstawi szkielety wszystkich składowych danego interfejsu. 49

50 Waldemar Bartyna Konflikty nazw Klasa lub struktura może implementować dowolną liczbę interfejsów. Pociąga to za sobą możliwość pojawiania się konfliktów nazw składowych kilku interfejsów. Załóżmy, że mamy zdefiniowane następujące trzy interfejsy: 50 Każdy z nich definiuje składową o tej samej nazwie Draw()

51 Waldemar Bartyna Konflikty nazw c. d. W przypadku zdefiniowania jednej klasy wspierającej te trzy interfejsy, kompilator zezwoliłby na poniższą implementację tych interfejsów (czyli przy pomocy jednej metody o nazwie Draw()). Mimo, że ten sposób nie powoduje błędów, nie pozwala nam jednak na korzystanie z zalet wykorzystania interfejsów. 51

52 Waldemar Bartyna Konflikty nazw c. d. 2 Bez względu na to, jaki interfejs uzyskamy z obiektu, wykonana zostaje zawsze ta sama metoda. A oczywiście, np. drukowanie i zapis do pamięci są realizowane w różny sposób. 52

53 Waldemar Bartyna Jawna implementacja interfejsu Konflikt nazw można rozwiązać stosując jawną implementację nazw. Nazwy metod poprzedzamy nazwami odpowiednich interfejsów. 53

54 Waldemar Bartyna Jawna implementacja interfejsu c. d. Jak można zauważyć, przy implementowanych metodach nie podaliśmy modyfikatorów dostępu. W przypadku jawnej implementacji interfejsu, składowe automatycznie stają się prywatne (i nie możemy tego zmienić). Jedynym sposobem odwołania się do tak zdefiniowanej metody jest rzutowanie obiektu na odpowiedni interfejs. Przykład znajduje się na następnym slajdzie. 54

55 Waldemar Bartyna Jawna implementacja interfejsu c. d. 2 Jawną implementację interfejsu możemy również stosować wtedy, gdy chcemy „ukryć” pewną funkcjonalność definiowanej klasy. Z funkcjonalność tej możemy korzystać poprzez rzutowanie danego obiektu na odpowiedni (nam znany) interfejs. 55

56 Waldemar Bartyna Definiowanie hierarchii interfejsów Podobnie, jak w przypadku klasy, interfejsu również można definiować poprzez hierarchie. Interfejs, rozszerzając inny interfejs, dziedziczy deklarację jego abstrakcyjnych składowych. W odróżnieniu od klas, interfejsy nie dziedziczą żadnej (domyślnej) implementacji. Interfejs rozszerza po prostu wskazany interfejs o dodatkowe abstrakcyjne składowe. 56

57 Waldemar Bartyna Definiowanie hierarchii interfejsów c. d. Hierarchie interfejsów są szczególnie użyteczne w przypadku, gdy chcemy rozszerzyć funkcjonalność istniejącego interfejsu, ale bez dokonywanie zmian w istniejącym kodzie. Przykładowa hierarchia interfejsów może wyglądać w następujący sposób: 57

58 Waldemar Bartyna Definiowanie hierarchii interfejsów c. d. 2 Jeżeli pewien typ chce wspierać interfejs, musi on zaimplementować składowe wszystkich interfejsów rozszerzanych przez dany interfejs. 58

59 Waldemar Bartyna Definiowanie hierarchii interfejsów c. d. 3 Po powołaniu obiektu klasy wspierającej interfejs dziedziczący po innych interfejsach, możemy: –wywołać na poziomie obiektu składowe każdego z interfejsów w łańcuchu dziedziczenia, –uzyskać jawny dostęp do każdego z tych interfejsów poprzez rzutowanie. 59

60 Waldemar Bartyna Rozszerzanie interfejsów W odróżnieniu od klas, w przypadku interfejsów możliwe jest wskazanie wielu rozszerzanych interfejsów.. 60

61 Waldemar Bartyna Rozszerzanie interfejsów c.d. Jeżeli chcemy zdefiniować klasę wspierającą IShape, to ile metod musimy zaimplementować? 61

62 Waldemar Bartyna Rozszerzanie interfejsów c.d. 2 Prawidłowa odpowiedz: To zależy. Jeżeli chcemy stworzyć wspólną implementację metody Draw(), wystarczy dostarczyć tylko trzy metody. 62

63 Waldemar Bartyna Rozszerzanie interfejsów c.d. 3 Jeżeli chcemy dostarczyć różne implementacje metody Draw() dla każdego z interfejsów (co oczywiście jest bardziej sensowne), zaimplementujemy cztery składowe. 63

64 Waldemar Bartyna Podsumowanie Interfejsy stanowią fundamentalny aspekt platformy.NET Framework. Praca z interfejsami jest składową tworzenia aplikacji niezależnie od jej zastosowania (sieciowe, GUI, biblioteki do obsługi baz danych, itp. ). Interfejsy w odróżnieniu od klas bazowych stosujemy: –gdy w naszej hierarchii klas tylko ich część ma wspierać pewne wspólne zachowanie, –gdy chcemy zamodelować wspólne zachowanie dla klas zdefiniowanych w różnych hierarchiach klas (nie mających wspólnego obiektu bazowego poza klasą System.Object). 64

65 Waldemar Bartyna 65 Implementacja standardowych interfejsów IEnumerable, IEnumerator

66 Waldemar Bartyna Tworzenie typów wyliczeniowych Interfejsy IEnumerable i IEnumerator wykorzystywane są w konstrukcji języka C# znanej jako pętla foreach. Pozwala ona nam przechodzić po kolejnych elementach typów tablicowych. W rzeczywistości każdy typ posiadający zaimplementowaną metodę GetEnumerator() może być używany w konstrukcji foreach. 66

67 Waldemar Bartyna Tworzenie typów wyliczeniowych c. d. Aby zademonstrować sposób dostosowania typu do potrzeb pętli foreach, zmodyfikujemy klasę Car dodając nowe właściwości. 67

68 Waldemar Bartyna Tworzenie typów wyliczeniowych c. d. 2 Oraz stworzymy nową klasę reprezentującą garaż przechowujący listę samochodów za pomocą obiektu klasy System.Array 68

69 Waldemar Bartyna Tworzenie typów wyliczeniowych c. d. 3 Chcemy osiągnąć możliwość przejścia po wszystkich samochodach w garażu za pomocą konstrukcji foreach. Niestety, kompilator poinformuje nas, że klasa Garage nie posiada metody GetEnumerator(). 69

70 Waldemar Bartyna Interfejs IEnumerable i IEnumerator Metoda GetEnumerator() jest sformalizowana w interfejsie IEnumerable w przestrzeni nazw System.Collections. Typ, który implementuje ten interfejs, potrafi zwracać kolejne elementy zawarte w obiekcie tej klasy (np. w pętli foreach). Metoda GetEnumeator() zwraca referencję do kolejnego interfejsu: System.Collections.IEnumerator. Interfejs ten zapewnia infrastrukturę pozwalającą na przechodzenie po wewnętrznych obiektach zawartych w kontenerze kompatybilnym z interfejsem IEnumerable. 70

71 Waldemar Bartyna Interfejs IEnumerable i IEnumerator c. d. Możemy zapewnić własną implementację składowych GetEnumerator(), MoveNext(), Current, i Reset(). Prostszym rozwiązaniem, możliwym do zastosowania w większości przypadków, jest delegacja żądania do wewnętrznego typu tablicowego (typy te oczywiście wspierają interfejs IEnumerable). 71

72 Waldemar Bartyna Interfejs IEnumerable i IEnumerator c. d. 2 Dzięki wsparciu dla interfejsu IEnumerable, obiekty danej klasy mogą być używane w pętli foreach. Można również korzystać „ręcznie” z typu IEnumerator, o ile metoda GetEnumerator() została zdefiniowana jako publiczna. Jeżeli chcemy ukryć tą nową funkcjonalność klasy na poziomie obiektu, stosujemy jawną implementację interfejsu. 72

73 Waldemar Bartyna Definiowanie iteratora Od wersji platformy.NET 2.0 mamy jeszcze jeden sposób przystosowania klasy do jej używania w pętli foreach; poprzez zdefiniowanie iteratora. Iterator to składowa klasy, która specyfikuje, w jaki sposób powinny być zwracane wewnętrzne elementy klasy na żądanie pętli foreach. Metoda ta musi nazywać się GetEnumerator() i zwracać wartość typu IEnumerator, ale klasa nie musi wspierać żadnego interfejsu. 73

74 Waldemar Bartyna Słowo kluczowe yield return Słowo kluczowe yield return jest wykorzystane do specyfikowania wartości, które mają zostać zwrócone do wywołującej pętli foreach. Gdy program dotrze do instrukcji yield return, bieżące miejsce jest zapamiętywane. Po kolejnym odwołaniu się do iteratora, jego wykonanie będzie kontynuowane od zapamiętanego miejsca. Metoda iteratora nie musi wykorzystywać wewnętrznej pętli foreach. Można ja również zdefiniować w poniżej przedstawiony sposób. 74

75 Waldemar Bartyna Definiowanie nazwanego iteratora Słowo kluczowe yield return może być używane w dowolnej metodzie. Takie metody, określane mianem nazwanych iteratorów, mogą przyjmować dowolną liczbę argumentów. Należy pamiętać, że nazwany iterator zwraca typ IEnumerable, a nie IEnumerator. 75

76 Waldemar Bartyna Definiowanie nazwanego iteratora c. d. Nazwane iteratory pozwalają nam na posiadanie kilku sposobów zwracania wewnętrznych elementów. 76

77 Waldemar Bartyna 77 Implementacja standardowych interfejsówIClonable

78 Waldemar Bartyna Klonowanie obiektów Klonowanie wykorzystuje się w celu utworzenia identycznej kopii danego obiektu. Późniejsza zmiana obiektu oryginalnego nie powinna wpływać w żaden sposób na stan wewnętrzny utworzonych kopii (klonów). Klasy, których obiekty można klonować implementują interfejs IClonable definiujący pojedyncza metodę Clone(). 78

79 Waldemar Bartyna Płytkie klonowanie W przypadku klas definiujących składowe nie będące referencjami do innych typów (tylko bezpośrednio wartościami) wystarczy zastosowanie płytkiego klonowania (kopiowania wartości składowa po składowej). 79

80 Waldemar Bartyna Głębokie klonowanie W przypadku płytkiego klonowania, metodę Clone() możemy prosto zaimplementować za pomocą metody odziedziczonej po System.Object: MemberwiseClone(). W przypadku, gdy klasa posiada referencje do innych typów musimy zastosować głębokie klonowanie. Dodajmy do naszego przykłady dodatkowy typ. 80

81 Waldemar Bartyna Głębokie klonowanie c. d. 81

82 Waldemar Bartyna Głębokie klonowanie c. d Przykładowy kod sprawdzający rezultat płytkiego planowania w przypadku istnienia w klasie referencji do innych typów.

83 Waldemar Bartyna Głębokie klonowanie c. d. 3 Poprzednia implementacja metody Clone() nie dała oczekiwanego rezultatu, ponieważ nie uwzględniała kopiowania obiektów wskazywanych przez referencję w definicji klonowanego obiektu. Aby uwzględnić te referencje, możemy postąpić w następujący sposób: 83

84 Waldemar Bartyna 84 Implementacja standardowych interfejsówIComparable

85 Waldemar Bartyna Interfejs System.IComparable definiuje zachowanie pozwalające na sortowanie obiektów wspierających ten interfejs według wybranego klucza. Klasa System.Array definiuje statyczną metodę Sort(). Realizuje ona sortowanie tablicy obiektów implementujących interfejs IComparable. Porównywanie obiektów jednej klasy 85

86 Waldemar Bartyna Porównywanie obiektów jednej klasy c. d. 86 Dodajmy do naszej klasy nowe pole i właściwość, według której będziemy sortować obiekty tej klasy. Stwórzmy tablice przykładowych obiektów tej klasy

87 Waldemar Bartyna Implementacja interfejsu IComparable Klasa wspierająca interfejs IComparable musi zaimplementować metodę CompareTo(). Od implementacji tej metody zależy sposób porównywania obiektów tej klasy. 87

88 Waldemar Bartyna Implementacja interfejsu IComparable c. d. Wartości zwracane przez metodę CompareTo():  Liczba mniejsza od zera – ta instancja obiektu jest przed wskazanym obiektem w porządku sortowania,  Zero – ta instancje jest równa wskazanemu obiektowi,  Liczba większa od zera – ta instancja obiektu jest po wskazanym obiekcie w porządku sortowania, Możemy uprościć porównanie wykorzystując fakt, że typ int również implementuj interfejs IComparable. 88

89 Waldemar Bartyna Implementacja interfejsu IComparable c. d. 2 Możemy teraz przetestować rezultat zaimplementowania interfejsu IComparable w następujący sposób: 89

90 Waldemar Bartyna Możliwe jest również obsłużenie w jednej klasie kilku sposobów sortowania jej obiektów. Służy do tego interfejs IComparer zdefiniowany w przestrzeni nazw System.Collactions. W przeciwieństwie do interfejsu ICoparable, interfejs IComparer nie jest typowo implementowany w klasie, której obiekty porównujemy. Interfejs ten implementują odpowiednie klasy pomocnicze, jedna dla każdego sposobu sortowania. Implementacja interfejsu IComparer 90

91 Waldemar Bartyna Implementacja interfejsu IComparer c. d. Do tej pory, w typie Car mamy zdefiniowane sortowanie w oparciu o identyfikator samochodu. Zdefiniujemy teraz klasę pomocnicza (implementującą interfejs IComparer), która będzie porównywać obiekty w oparciu o ich „imię”. 91

92 Waldemar Bartyna Implementacja interfejsu IComparer c. d. 2 Teraz, kod użytkownika może skorzystać z nowego sposobu sortowania. Klasa System.Array posiada metodę Sort(), której argumentami są: tablica do sortowania oraz interfejs IComparer. 92

93 Waldemar Bartyna Zalecanym sposobem postępowania fest dodanie do klasy właściwości tylko do odczytu, która będzie zwracać odpowiednią klasę pomocniczą realizującą zadany sposób sortowania. W ten sposób, użytkownik może implementować sortowanie w oparciu o odpowiednio nazwane właściwości klasy, a nie osobną klasę pomocniczą, o której musi wcześniej wiedzieć. Implementacja interfejsu IComparer c. d. 3 93

94 Waldemar Bartyna 94 Wykorzystanie interfejsów jako mechanizmu komunikacji Interfejsy

95 Waldemar Bartyna Interfejsy wywołań zwrotnych Interfejsy wykorzystywane są nie tylko do umożliwiania polimorfizmu w przypadku klas należących do różnych hierarchii, przestrzeni nazw i pakietów. Interfejsy mogą również być stosowane w roli mechanizmu wywołań zwrotnych. W C#, delegaty są dedykowanymi rozwiązaniami do komunikacji między obiektami. Mechanizm wywołań zwrotnych zrealizowany za pomocą interfejsów omówimy na przykładzie znanej klasy Car. Rozpoczniemy od stworzenia interfejsu. 95

96 Waldemar Bartyna Interfejsy wywołań zwrotnych c. d. Interfejsy często nie są implementowane przez klasy biorące udział w komunikacji, ale przez klasę pomocniczą (ang. sink). Klasa zgłaszająca zdarzenia będzie wywoływać metody klasy pomocniczej w przypadku zajścia odpowiednich okoliczności. 96

97 Waldemar Bartyna Interfejsy wywołań zwrotnych c. d. 2 Kolejnym krokiem jest zdefiniowanie metod pozwalających na przekazanie (i wycofanie) referencji do interfejsu do klasy, która ma nas informować o zdarzeniach. Dodatkowo wprowadzamy możliwość rejestracji wielu interfejsów wywołań zwrotnych, co umożliwia informowanie wielu zainteresowanych (multicast). 97

98 Waldemar Bartyna Interfejsy wywołań zwrotnych c. d W klasie Car dodajemy przechodzenie po liście zarejestrowanych interfejsów i zgłaszanie (wywoływanie) odpowiednich zdarzeń.

99 Waldemar Bartyna Interfejsy wywołań zwrotnych c. d. 4 Posiadając taką infrastrukturę, możemy zaimplementować metodę pozwalającą na odbieranie informacji o zdarzeniach od obiektów klasy Car. 99

100 Waldemar Bartyna Pytania egzaminacyjne (Zestaw06) 27.Co to jest interfejs? 28.Czym interfejs różni się od interfejsu polimorficznego? 29.W jaki sposób implementujemy interfejsy? 30.Do czego służą słowa kluczowe as i is? 31.Czym się różni jawna i niejawna implementacja interfejsów? 32.Co trzeba zrobić, aby po zawartości naszej klasy można było iterować za pomocą pętli foreach 33.Czym się różnie płytkie i głębokie klonowanie? 34.Co trzeba zrobić, aby kolekcję naszych obiektów można było posortować? 100

101 Waldemar Bartyna 101 Dziękuję za uwagę


Pobierz ppt "Waldemar Bartyna 1 Programowanie zaawansowane Interfejsy."

Podobne prezentacje


Reklamy Google