Programowanie w środowiskach graficznych Wykład 4 Język C# cd 1
Wyjątki Służą do raportowania i obsługi sytuacji wyjątkowych (błędów, które można w jakiś sposób naprawić). Instrukcje, które mogą się z jakichś przyczyn nie udać, jak np. pobieranie danych od użytkownika, niepewne rzutowanie, operacje wejścia/wyjścia, alokację dużej ilości pamięci obudowujemy blokiem try. object o2 = null; try int i2 = (int)o2; // Casting Error 2
Hierarchia klas wyjątków w C# 3
Wyjątki catch, finally, throw Bloki catch przechwytują rzucony wyjątek i starają się go obsłużyć (lub zaraportować użytkownikowi); Pierwszy catch pasujący typem obsługuje wyjątek; Blok finally służy do końcowych porządków (np. zwolnienie zasobów); throw: słowo kluczowe umożliwiające (niekiedy ponowne) zgłoszenie wyjątku. catch (FileNotFoundException e) // FileNotFoundExceptions are handled here. catch (IOException e) if (e.source!= null) Console.WriteLine("IOException source: 0", e.source); throw; 4
Wyjątki catch Dopuszczalne jest użycie samego catch try catch 5
Wskazana jest odpowiednia kolejność catch try catch (Exception exception) catch (IOException ioexception) // tutaj nigdy nie dojdziemy, bo IOException jest // specjalizacją Exception, więc poprzedni catch // wyłapie też każdy wyjątek klasy IOException 6
Delegaty Odpowiedniki wskaźników do funkcji z C++ public delegate int KtoryMniejszy (object o1, object o2); KtoryMniejszyPorownanie += new KtoryMniejszy (FunkcjaPorownawcza); public int FunkcjaPorownawcza (object o1, object o2) return (o1>o2); Tab.sort(Porownanie); 7
Delegaty i zdarzenia Delegaty są wykorzystywane do obsługi zdarzeń. Visual Studio automatycznie je deklaruje i wrzuca do pliku designera, np. Form1.Designer.cs delegate void EventHandler (object sender, EventArgs e); button1.click += new EventHandler (bsubmit_click); void bsubmit_click(object sender, EventArgs e) Button nadawca = sender as Button; 8
Generyczność (programowanie generyczne) Generyczność pozwala klasom, strukturom, interfejsom, delegatom i metodom być sparametryzowanym przez typ. Mechanizm podobny do szablonów w C++. 9
Dlaczego generyczność? Przykład: stos obiektów dowolnej klasy. Kompilator nie wie, jakiej klasy będą obiekty na stosie Wymaga więc jawnego rzutowania, by wykonać kod specyficzny dla rzeczywistej klasy obiektu. public class Stackobject[] items;int count; public void Push(object item)... public object Pop()... Stack stack = new Stack(); stack.push(new Customer ("Nowak")); Customer c = (Customer) stack.pop(); c.obsłużklienta(); 10
A teraz to samo generycznie: parametryzujemy stos typem T i tworzymy stos Customer-ów: public class Stack<T> T[] items; int count; public void Push(T item)... public T Pop()... Stack<Customer> stack = new Stack<Customer>();stack.Push(new Customer( Adam Słodowy )); Customer serviced = stack.pop(); serviced.obsłużklienta(); Rzutowanie może być niejawne, bo kompilator wie, że na stosie wszystkie obiekty muszą być klasy Customer. 11
Generyczność dwa typy public class Dictionary<K,V> public void Add(K key, V value)... public V this[k key]... Dictionary<string,Customer> dict=new Dictionary<string,Customer>(); dict.add("pit", new Customer("Peter Hall")); Customer c = dict["pit"]; Tutaj używamy napisów (klasa string) do indeksowania obiektów klasy Customer (tzw. tablica asocjacyjna). 12
Generyczność - kompilacja Kompiluje się do IL (instrukcje i metadane) Proces tworzenia generic type instantion Po jednej instancji dla każdego używanego w kodzie typu prostego Jedna instancja dla typów referencyjnych 13
Generyczność ograniczenia na parametry (1/4) public class Dictionary<K,V> public void Add(K key, V value)... if (key.compareto(x) < 0)... // błąd kompilacji Kompilator sygnalizuje błędem, że nieznana mu klasa K prawdopodobnie nie obsługuje porównań (wywołań CompareTo()). 14
Generyczność ograniczenia na parametry (2/4) I rozwiązanie: jawne rzutowanie public class Dictionary<K,V> public void Add(K key, V value)... if (((IComparable)key).CompareTo(x) <0)...... 15
Generyczność ograniczenia na parametry (3/4) II rozwiązanie: ograniczenia na parametry generyczne public class Dictionary<K, V> where K : IComparable public void Add(K key, V value)... if (key.compareto(x) < 0)... 16
Generyczność ograniczenia na parametry (4/4) Ograniczenia mogą dotyczyć wielu interfejsów, jednej klasy, a także konstruktora new(): public class EntityTable<K, E> where K : IComparable<K>, IPersistable where E : Entity, new() public void Add(K key, E entity)... if (key.compareto(x) < 0)... 17
Metody generyczne 1/2 Możemy parametryzować także metody: void PushMultiple<T> (Stack<T> stack, params T[] values) foreach (T value in values) stack.push(value);... Stack<int> stack = new Stack<int>(); PushMultiple<int>(stack, 1, 2, 3, 4); 18
Metody generyczne 2/2 type inferencing wywołanie metody z odpowiednim typem generycznym na podstawie dedukcji parametrów: Stack<int> stack = new Stack<int>(); PushMultiple(stack, 1, 2, 3, 4); //wywołanie dla int tak jak dla klas możemy wprowadzać też ograniczenia dla metod: public class MyClass public void SomeMethod<T>(T t) where T :IComparable<T>... 19
Typy generyczne i rzutowanie 1/3 Niejawnie możemy rzutować do object i do typów występujących w ograniczeniach: class MyClass<T> where T : BaseClass,ISomeInterface void SomeMethod(T t) ISomeInterface obj1 = t; BaseClass obj2 = t; object obj3 = t; 20
Typy generyczne i rzutowanie 2/3 Jawnie rzutować możemy tylko do interfejsów: class MyClass<T> void SomeMethod(T t) ISomeInterface obj1 = (ISomeInterface)t; SomeClass obj2 = (SomeClass)t; // Compiles // ERROR! 21
Typy generyczne i rzutowanie 3/3 Możemy ominąć to ograniczenie używając zmiennej pomocniczej typu object: class MyClass<T> void SomeMethod(T t) object temp = t; SomeClass obj = (SomeClass)temp; Warto używać operatorów is i as. 22
Dziedziczenie i generics 1/2 Jeżeli klasa dziedziczy z klasy generycznej to musi być podany konkretny typ jako parametr: public class BaseClass<T>... public class SubClass : BaseClass<int>... Chyba że klasa i podklasa są sparametryzowane tym samym parametrem: public class SubClass<T> : BaseClass<T>... 23
Dziedziczenie i generics 2/2 Przy dziedziczeniu należy powtarzać ograniczenia w podklasach: public class BaseClass<T> where T : ISomeInterface... public class SubClass<T> : BaseClass<T> where T : ISomeInterface... To samo dotyczy metod wirtualnych: public class BaseClass public virtual void SomeMethod<T>(T t) where T : new()... public class SubClass : BaseClass public override void SomeMethod<T>(T t) where T : new()... 24
Generics i delegaty Tak jak klasy struktury i interfejsy możemy sparametryzować typem generycznym także delegaty: public delegate void GenericDelegate<T>(T t); public class MyClass public void SomeMethod(int number)... MyClass obj = new MyClass(); GenericDelegate<int> del; del = new GenericDelegate<int>(obj.SomeMethod); del(3); 25
Kolekcje generyczne i niegeneryczne System.Collections.Generics i odpowiedniki System.Collections Comparer<T> Dictionary<K,T> List<T> Queue<T> SortedDictionary<K,T> Stack<T> Comparer HashTable ArrayList Queue SortedList Stack IComparable<T> IComparable 26
Metody anonimowe 1/3 Standardowy handler zdarzenia w osobnej metod class InputForm: Form ListBox listbox; TextBox textbox; Button addbutton; public MyForm() listbox = new ListBox(...); textbox = new TextBox(...); addbutton = new Button(...); addbutton.click += new EventHandler(AddClick); void AddClick(object sender, EventArgs e) listbox.items.add(textbox.text); 27
Metody anonimowe 2/3 Ten sam przykład z użyciem metody anonimowej: class InputForm: Form ListBox listbox; TextBox textbox; Button addbutton; public MyForm() listbox = new ListBox(...); textbox = new TextBox(...); addbutton = new Button(...); addbutton.click += delegatelistbox.items.add(textbox.text);; Kod jest krótszy, ale nie możemy używać tego samego handlera dla wielu kontrolek 28
Metody anonimowe 3/3 Możemy także używać metod anonimowych do wpisywania funkcji in-line delegate double Function (double x); class Test static double[] Apply(double[] a, Function f) static double[] MultiplyAllBy(double[] a, double factor) return Apply(a, delegate(double x) return x*factor; ); static void Main() double[] a = 0.0, 0.5, 1.0; double[] squares = Apply(a, delegate(double x) return x*x; ); double[] doubles = MultiplyAllBy(a, 2.0); 29
Iteratory 1/3 Żeby można było użyć pętli foreach na kolekcji, to kolekcja musi spełniać warunki: Kolekcja musi implementować interfejs IEnumerable. Kolekcja musi mieć bezparametrową metodę GetEnumerator(). 30
Iteratory 2/3 Numeratory są trudne do zaimplementowania. Sprawę ułatwiają nam ITERATORY: public class Stack<T>: IEnumerable<T> T[] items; int count; public void Push(T data)... public T Pop()... public IEnumerator<T> GetEnumerator() for (int i = count 1; i >= 0; --i) yield return items[i]; Mamy też do dyspozycji instrukcję yield break 31
Iteratory 3/3 Możemy także zdefiniować kilka iteratorów dla 1 kolekcji: public class Stack<T>: IEnumerable<T> public IEnumerator<T> GetEnumerator() for (int i = count 1; i >= 0; --i) yield return items[i]; public IEnumerable<T> TopToBottom get return this; public IEnumerable<T> BottomToTop get for (int i = 0; i < count; i++) yield return items[i]; 32
Iteratory 3/3 cd.. Użycie: class Test static void Main() Stack<int> stack = new Stack<int>(); for (int i = 0; i < 10; i++) stack.push(i); foreach (int i in stack.toptobottom) Console.Write("0 ", i); foreach (int i in stack.bottomtotop) Console.Write("0 ", i); 33
Klasy częściowe 1/2 Podział zawartości 1 klasy w więcej niż jednym pliku np: plik1.cs public partial class Customer private int id; private string name; private string address; private List<Order> orders; public Customer()... 34
Klasy częściowe 2/2 Plik2.cs public partial class Customer public void SubmitOrder(Order order) orders.add(order); public bool HasOutstandingOrders() return orders.count > 0; 35
Nullable type 1/3 Wprowadza dla wszystkich typów prostych dodatkową możliwą wartość null. int? Jest to typ wbudowany + wartość null. HasValue - właściwość, która zwraca nam true or false jeżeli typ jest nullable lub nie. 36
Nullable type 2/3 Operacje arytmetyczne z null-em dają null Operatory >,<,<=, >= zwracają false, jeżeli któryś z argumentów jest null. Nowy operator?? a??b rezultatem jest a jeżeli a jest not-null i b w przeciwnym przypadku. 37
Nullable type 3/3 Przykład: int? x = GetNullableInt(); int? y = GetNullableInt(); int? z = x?? y; int i = z?? -1; Operator ten działa też dla typów referencyjnych: string s = GetStringValue(); Console.WriteLine(s?? "Unspecified"); 38
Podsumowanie Duży wachlarz konstrukcji. Różne paradygmaty (komponentowy, obiektowy, funkcyjny, proceduralny) Uznawany za najlepszy język do programowania na platformie.net 39