Skip to content

Zadania (21-30)

Operator is sprawdza, czy obiekt jest danego typu (lub typu pochodnego), i zwraca wartość logiczną true/false. Od C# 7 łączy się go z dopasowaniem wzorca, dzięki czemu w jednym kroku można sprawdzić typ i przypisać obiekt do nowej zmiennej.

Operator as próbuje rzutować obiekt na wskazany typ referencyjny lub typ dopuszczający null. Jeśli rzutowanie się nie powiedzie, zwraca null zamiast rzucać wyjątek (w przeciwieństwie do zwykłego rzutowania w nawiasach, które zgłasza InvalidCastException). Dlatego as jest bezpieczniejszy, gdy nie mamy pewności co do typu.

object o = "tekst";
// is sprawdza typ i zwraca bool; tu od razu z przypisaniem do zmiennej s
if (o is string s)
{
Console.WriteLine(s.Length);
}
// as próbuje rzutować; przy niepowodzeniu zwraca null (bez wyjątku)
string t = o as string;
if (t != null)
{
Console.WriteLine(t);
}

Skrót do zapamiętania: is sprawdza typ i zwraca bool (z możliwym dopasowaniem wzorca). as próbuje rzutować i przy niepowodzeniu zwraca null zamiast wyjątku.

  • is: test typu -> bool; od C# 7 z pattern matching (o is string s).
  • as: bezpieczne rzutowanie typów referencyjnych/nullable; niepowodzenie -> null.
  • Zwykłe rzutowanie (Typ)o przy błędzie rzuca InvalidCastException.
  • as używamy, gdy nie mamy pewności co do typu.

Konstrukcja match (wzorce aktywne) i funkcje rekurencyjne w F#

Section titled “Konstrukcja match (wzorce aktywne) i funkcje rekurencyjne w F#”

Konstrukcja match … with w F# to rozbudowane dopasowanie wzorców, działające jak wielokrotny switch, ale potrafiące dodatkowo rozbijać strukturę danych i wiązać jej części ze zmiennymi. Każdy przypadek to wzorzec poprzedzony kreską pionową, a _ oznacza dowolną wartość.

Wzorce aktywne (active patterns) pozwalają definiować własne kategorie dopasowania w nawiasach z kreskami pionowymi, np. podział liczb na parzyste i nieparzyste. Funkcje rekurencyjne deklaruje się słowem let rec i często łączy z match, gdzie jeden przypadek jest warunkiem zakończenia (bazowym), a drugi zawiera wywołanie rekurencyjne.

// funkcja rekurencyjna - oznaczona slowem rec
let rec factorial n =
// dopasowanie wzorca do wartosci n
match n with
// przypadek bazowy konczacy rekurencje
| 0 -> 1
// przypadek ogolny z wywolaniem rekurencyjnym
| _ -> n * factorial (n - 1)
// aktywny wzorzec dzielacy liczby na Even i Odd
let (|Even|Odd|) n =
if n % 2 = 0 then Even else Odd
// uzycie aktywnego wzorca w match
let describe n =
match n with
| Even -> "parzysta"
| Odd -> "nieparzysta"

Skrót do zapamiętania: match…with to dopasowanie wzorców (switch z rozbijaniem struktury). Wzorce aktywne (|A|B|) to własne kategorie dopasowania. Funkcje rekurencyjne to let rec, zwykle z przypadkiem bazowym w match.

  • match…with: dopasowanie wzorca, | na przypadek, _ = dowolny.
  • Potrafi rozbijać strukturę i wiązać części ze zmiennymi.
  • Wzorce aktywne: własne wzorce definiowane jako (|A|B|).
  • Rekurencja: let rec, przypadek bazowy + wywołanie rekurencyjne.

Program w F# do obchodzenia drzewa binarnego (post-order oraz dwa pozostałe porządki)

Section titled “Program w F# do obchodzenia drzewa binarnego (post-order oraz dwa pozostałe porządki)”

Drzewo binarne reprezentujemy jako typ unii: węzeł z wartością i dwoma poddrzewami albo liść (pusty). Obchodzenie realizujemy funkcjami rekurencyjnymi, które różnią się jedynie kolejnością łączenia wyników. W post-order odwiedzamy najpierw lewe poddrzewo, potem prawe, na końcu korzeń. W in-order: lewe, korzeń, prawe. W pre-order: korzeń, lewe, prawe. Operator @ łączy listy.

// definicja drzewa binarnego: lisc (pusty) lub wezel z wartoscia i dwoma poddrzewami
type Tree =
| Leaf
| Node of int * Tree * Tree
// post-order: lewe, prawe, korzen
let rec postOrder tree =
match tree with
// pusty lisc daje pusta liste
| Leaf -> []
// najpierw lewe poddrzewo, potem prawe, na koncu wartosc wezla
| Node(v, l, r) -> postOrder l @ postOrder r @ [v]
// in-order: lewe, korzen, prawe
let rec inOrder tree =
match tree with
| Leaf -> []
// lewe poddrzewo, wartosc, prawe poddrzewo
| Node(v, l, r) -> inOrder l @ [v] @ inOrder r
// pre-order: korzen, lewe, prawe
let rec preOrder tree =
match tree with
| Leaf -> []
// najpierw wartosc, potem lewe i prawe poddrzewo
| Node(v, l, r) -> [v] @ preOrder l @ preOrder r
// przykladowe drzewo
let tree = Node(1, Node(2, Leaf, Leaf), Node(3, Leaf, Leaf))
// wypisanie wynikow obchodzenia
printfn "post-order: %A" (postOrder tree)
printfn "in-order: %A" (inOrder tree)
printfn "pre-order: %A" (preOrder tree)

Skrót do zapamiętania: Drzewo to typ unii (Leaf | Node of wartosc, lewe, prawe). Trzy rekurencyjne funkcje różnią się tylko kolejnością: post = L,P,korzeń; in = L,korzeń,P; pre = korzeń,L,P. Listy łączy @.

  • Typ: Leaf | Node of int * Tree * Tree.
  • post-order: postOrder l @ postOrder r @ [v].
  • in-order: inOrder l @ [v] @ inOrder r.
  • pre-order: [v] @ preOrder l @ preOrder r.
  • Wszystkie rekurencyjne (let rec) z dopasowaniem match.

W F# operatory można przeciążać, definiując je jako statyczne składowe typu (klasy lub rekordu) o nazwie operatora w nawiasach, np. (+). Działa to analogicznie do C#: gdy użyjemy operatora na obiektach danego typu, wywoła się nasza definicja. Można też definiować własne operatory infiksowe na poziomie modułu.

// typ Vector z przecionym operatorem +
type Vector(x: int, y: int) =
// wlasnosci tylko do odczytu
member this.X = x
member this.Y = y
// przeciazenie operatora + jako statyczna skladowa typu
static member (+) (a: Vector, b: Vector) =
// tworzymy nowy wektor jako sume skladowych
Vector(a.X + b.X, a.Y + b.Y)
// uzycie operatora na obiektach Vector
let v = Vector(1, 2) + Vector(3, 4)

Skrót do zapamiętania: W F# operator przeciąża się jako statyczną składową typu o nazwie operatora w nawiasach, np. static member (+) (a, b) = .... Można też tworzyć własne operatory infiksowe.

  • Składnia: static member (+) (a, b) = ... wewnątrz typu.
  • Działa po użyciu operatora na obiektach tego typu.
  • Można definiować własne operatory infiksowe (na poziomie modułu).
  • Idea jak w C#, inna składnia.

Modele programowania ADO.NET - dwa modele dostępu do baz danych

Section titled “Modele programowania ADO.NET - dwa modele dostępu do baz danych”

ADO.NET udostępnia dwa podstawowe modele dostępu do danych: połączeniowy i bezpołączeniowy. Różnią się one tym, czy połączenie z bazą jest utrzymywane przez cały czas pracy na danych.

Model połączeniowy opiera się na obiekcie DataReader. Połączenie z bazą jest otwarte przez cały czas odczytu, a dane czyta się strumieniowo, tylko do przodu i tylko do odczytu. Jest szybki i oszczędny pamięciowo, ale blokuje połączenie i nie pozwala swobodnie nawigować po danych.

Model bezpołączeniowy opiera się na obiektach DataSet i DataAdapter. Dane pobiera się do pamięci (DataSet to lokalna kopia danych), po czym połączenie jest zamykane. Można na nich pracować w trybie offline, a zmiany zsynchronizować z bazą później przez DataAdapter. Zużywa więcej pamięci, ale jest elastyczny i nie trzyma otwartego połączenia.

Skrót do zapamiętania: Połączeniowy (DataReader): połączenie cały czas otwarte, odczyt do przodu, szybki, mało pamięci. Bezpołączeniowy (DataSet/DataAdapter): dane w pamięci, połączenie zamykane, praca offline, więcej pamięci.

  • Połączeniowy: DataReader, otwarte połączenie, odczyt strumieniowy do przodu, szybki.
  • Bezpołączeniowy: DataSet + DataAdapter, dane w pamięci, połączenie zamykane.
  • Połączeniowy oszczędza pamięć, bezpołączeniowy daje elastyczność i pracę offline.
  • Łącznikiem z bazą jest Connection; polecenia wykonuje Command.

Opisać klasę Page stosowaną na poziomie aplikacji ASP.NET

Section titled “Opisać klasę Page stosowaną na poziomie aplikacji ASP.NET”

Klasa Page (System.Web.UI.Page) w ASP.NET Web Forms reprezentuje pojedynczą stronę .aspx. Każda strona aplikacji jest w istocie klasą dziedziczącą z Page, generowaną na podstawie pliku .aspx i powiązanego kodu (code-behind). Obiekt tej klasy jest tworzony przy każdym żądaniu strony.

Klasa Page zarządza cyklem życia strony i udostępnia kluczowe właściwości oraz zdarzenia. Najważniejsze właściwości to IsPostBack (czy strona jest wynikiem ponownego wysłania formularza), Request, Response, Session, ViewState i kontrolki strony. Najważniejsze zdarzenia cyklu życia to Init, Load, zdarzenia kontrolek oraz PreRender i Unload.

Skrót do zapamiętania: Page (System.Web.UI.Page) reprezentuje stronę .aspx; każda strona to klasa dziedzicząca z Page. Zarządza cyklem życia strony i udostępnia IsPostBack, Request, Response, Session oraz zdarzenia Init/Load/PreRender/Unload.

  • Reprezentuje stronę .aspx; każda strona dziedziczy z System.Web.UI.Page.
  • Tworzona przy każdym żądaniu, zarządza cyklem życia strony.
  • Właściwości: IsPostBack, Request, Response, Session, ViewState, kontrolki.
  • Zdarzenia: Init, Load, PreRender, Unload.

Transakcja to zbiór operacji wykonywanych jako jedna, niepodzielna całość, spełniający zasady ACID (atomowość, spójność, izolacja, trwałość). Albo wszystkie operacje się powiodą i zmiany zostaną zatwierdzone (commit), albo żadna i nastąpi wycofanie (rollback).

Pod względem zasięgu wyróżnia się transakcje lokalne i rozproszone. Transakcja lokalna obejmuje jeden zasób (jedną bazę danych) i jest zarządzana przez jeden menedżer transakcji. Transakcja rozproszona obejmuje wiele zasobów lub baz, koordynowanych przez wspólny menedżer (np. MSDTC), zwykle z protokołem zatwierdzania dwufazowego (two-phase commit). W .NET transakcje wygodnie obsługuje TransactionScope, który sam decyduje, czy potrzebna jest transakcja lokalna czy rozproszona.

Skrót do zapamiętania: Transakcja to niepodzielny zbiór operacji (ACID): commit albo rollback. Rodzaje wg zasięgu: lokalne (jeden zasób/baza, jeden menedżer) i rozproszone (wiele zasobów, koordynacja MSDTC, two-phase commit).

  • Transakcja = niepodzielny zbiór operacji, zasady ACID.
  • Commit (zatwierdzenie) albo rollback (wycofanie).
  • Lokalna: jeden zasób/baza, jeden menedżer transakcji.
  • Rozproszona: wiele zasobów, koordynator (MSDTC), zatwierdzanie dwufazowe.
  • W .NET: TransactionScope.

Wyjaśnić pojęcie plików XML i podać przykład takiego pliku

Section titled “Wyjaśnić pojęcie plików XML i podać przykład takiego pliku”

XML (Extensible Markup Language) to tekstowy, rozszerzalny format opisu danych w strukturze hierarchicznej (drzewiastej). Dane przechowuje się w znacznikach, które programista definiuje samodzielnie, dzięki czemu format jest samoopisujący i czytelny zarówno dla człowieka, jak i dla maszyny. XML służy głównie do przechowywania oraz wymiany danych między różnymi systemami niezależnie od platformy.

Plik XML ma jeden element nadrzędny (korzeń), wewnątrz którego zagnieżdżone są kolejne elementy. Elementy mogą mieć atrybuty i wartości tekstowe.

<?xml version="1.0" encoding="UTF-8"?>
<!-- element korzenia -->
<book>
<!-- elementy zagniezdzone z wartosciami -->
<title>Pan Tadeusz</title>
<author>Adam Mickiewicz</author>
<year>1834</year>
</book>

Skrót do zapamiętania: XML to tekstowy, hierarchiczny, samoopisujący format danych z definiowanymi przez użytkownika znacznikami; służy do przechowywania i wymiany danych między systemami. Ma jeden korzeń i zagnieżdżone elementy.

  • XML = tekstowy, rozszerzalny, hierarchiczny format danych.
  • Znaczniki definiuje użytkownik -> samoopisujący.
  • Jeden element korzenia, w nim zagnieżdżone elementy (z atrybutami i wartościami).
  • Zastosowanie: przechowywanie i wymiana danych niezależnie od platformy.

Czym jest serializacja obiektów w języku C#?

Section titled “Czym jest serializacja obiektów w języku C#?”

Serializacja to proces zamiany obiektu (jego stanu, czyli wartości pól) na postać nadającą się do zapisu lub przesłania, np. strumień bajtów, tekst XML albo JSON. Deserializacja to proces odwrotny: odtworzenie obiektu z takiej zapisanej postaci. Dzięki temu obiekty można zapisać do pliku, przesłać siecią lub przechować w pamięci podręcznej, a później je odtworzyć.

W C# do serializacji służą m.in. XmlSerializer (do XML), JsonSerializer (do JSON) oraz dawniej serializacja binarna. Klasy przeznaczone do serializacji oznaczano atrybutem [Serializable], a poszczególne pola można dostosowywać atrybutami.

using System.Text.Json;
// klasa, której obiekty chcemy serializować
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
// utworzenie obiektu
Person p = new Person { Name = "Ala", Age = 20 };
// serializacja obiektu do tekstu JSON
string json = JsonSerializer.Serialize(p);
// deserializacja - odtworzenie obiektu z tekstu JSON
Person restored = JsonSerializer.Deserialize<Person>(json);

Skrót do zapamiętania: Serializacja to zamiana obiektu na zapisywalną/przesyłalną postać (bajty, XML, JSON), deserializacja to proces odwrotny. W C#: XmlSerializer, JsonSerializer, atrybut [Serializable].

  • Serializacja: obiekt -> bajty/XML/JSON (do zapisu lub przesłania).
  • Deserializacja: postać zapisana -> obiekt.
  • Narzędzia: XmlSerializer, JsonSerializer (binarna - dawniej).
  • Zastosowanie: zapis do pliku, przesył siecią, cache.

Przedstawić sposób obsługi błędów jakie mogą pojawić się w aplikacjach ASP.NET

Section titled “Przedstawić sposób obsługi błędów jakie mogą pojawić się w aplikacjach ASP.NET”

W aplikacjach ASP.NET błędy obsługuje się na kilku poziomach. Najniższy to lokalna obsługa w kodzie blokami try-catch-finally, gdy spodziewamy się konkretnego błędu i chcemy go obsłużyć od razu. Wyższy poziom to obsługa na poziomie strony przez zdarzenie Page_Error, a najwyższy to obsługa globalna na poziomie całej aplikacji przez zdarzenie Application_Error w pliku Global.asax.

Dodatkowo w pliku konfiguracyjnym web.config sekcja customErrors pozwala zdefiniować przyjazne strony błędów wyświetlane użytkownikowi zamiast technicznych komunikatów (np. osobna strona dla błędu 404). Metoda Server.GetLastError() pozwala pobrać ostatni wyjątek, a Server.ClearError() go wyczyścić. W ASP.NET Core obsługę błędów realizuje się przez middleware (np. UseExceptionHandler).

Skrót do zapamiętania: Trzy poziomy: lokalnie try-catch, na poziomie strony Page_Error, globalnie Application_Error (Global.asax). Plus przyjazne strony błędów w web.config (customErrors) i Server.GetLastError().

  • Lokalnie: try-catch-finally w kodzie.
  • Na poziomie strony: zdarzenie Page_Error.
  • Globalnie: Application_Error w Global.asax.
  • web.config -> customErrors: przyjazne strony błędów (np. 404).
  • Server.GetLastError() pobiera ostatni wyjątek; w ASP.NET Core - middleware.