Algoritmy a programování III
Chapter contains:
1
PDF
1
Study text
Teacher recommends to study from 25/9/2023 to 1/10/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 2/10/2023 to 8/10/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 9/10/2023 to 15/10/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 16/10/2023 to 22/10/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 23/10/2023 to 29/10/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 30/10/2023 to 5/11/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 6/11/2023 to 12/11/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 13/11/2023 to 19/11/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 20/11/2023 to 26/11/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 27/11/2023 to 3/12/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 4/12/2023 to 10/12/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 11/12/2023 to 17/12/2023.
Chapter contains:
1
Study text
Teacher recommends to study from 18/12/2023 to 24/12/2023.

Týden 1

Úvodní hodina

Studenti se seznámí se základními principy objektově orientovaného programování v jazyce C#, principy dědičnosti, polymorfismus, zapouzdření, používání již dříve probraných struktur jako jsou cykly, rozhodovací struktury, vstupy a výstupy, výjimky, atd. se zaměřením na objektový přístup.

C# je vysokoúrovňový objektově orientovaný programovací jazyk vyvinutý firmou Microsoft zároveň s platformou .NET Framework, později schválený standardizačními komisemi ECMA a ISO. Microsoft založil C# na jazycích C++ a Java.
Zadání zápočtového projektu, konzultace se studenty, seznámení s prostředím Visual Studio. Postupně v jednotlivých kapitolách si uvedeme příklady. Pro začátečníky v programování v platformě Visual Studio, doporučuji též nalézt informace v oporách autora pro začátečníky(1).

Student bude schopen:
- definovat vlastní třídy a vytvářet jejich instance
- vytvářet potomky tříd a zvládat problematiku dědičnosti a vnořených tříd
- zvládat problematiku přetěžování metod a polymorfismu
- vytvářet a používat abstraktní třídy a šablony
- přetěžovt operátory
- spravovat výjimky
- vytvářet vlastní namespace
- vytvářet vlákna
- využívat základních prostředků WinAP

Osnova
  • 1. Třídy, objekty
  • 2. Metody
  • 3. Dědičnost, polymorfismus
  • 4. Zapouzdření
  • 5. Vnořené třídy
  • 6. Abstraktní třídy
  • 7. Přetěžování metod
  • 8. Šablony
  • 9. Přetěžování operátorů
  • 10. Výjimky
  • 11. Namespace
  • 12. Vlákna
  • 13. Úvod do WinAPI

Literatura:
    povinná literatura 1. Programování v C,.Blahuta Jiří, MVŠO 
Error: The referenced object does not exist or you do not have the right to read.
https://is.slu.cz/el/fpf/zima2023/UIIABP0030/4605194/Programovani_Blahuta_Opory_rSLU.pdf
    doporučená literatura
  • http://msdn.microsoft.cominfo
  • http://www.cplusplus.com
  • http://www.itnetwork.cz
  • https://stackoverflow.com
  • http://www.builder.cz/rubriky/c/c--/ucime-se-win-api-motivacni-uvod-155994cz
  • Prokop, J. Algoritmy v jazyku C a C++. Praha, 2015. ISBN 24797465. info
  • KRUGLINSKI, David, Scot WINGO a George SHEPHERD. Programujeme v Microsoft Visual C++. Praha: Computer Press, 2000. ISBN 80-7226-362-5.
Dokumentace k jazyku C# – začínáme, kurzy, referenční dokumentace | Microsoft Learn

Příklady na Learn C# - Free Interactive C# Tutorial (learncs.org)

Týden 2

C# je objektově orientovaný programovací jazyk orientovaný na komponenty. Jazyk C# poskytuje jazykové konstrukce pro přímou podporu těchto konceptů, díky kterým je jazyk C# přirozeným jazykem pro vytváření a používání softwarových komponent. Od svého původu přidává jazyk C# funkce pro podporu nových úloh a nově vznikajících postupů návrhu softwaru. V jádru je jazyk C# objektově orientovaný . Definujete typy a jejich chování.

Několik funkcí jazyka C# pomáhá vytvářet robustní a odolné aplikace. Uvolňování paměti automaticky uvolňuje paměť zabíranou nedostupnými nepoužívanými objekty. Typy s možnou hodnotou null chrání před proměnnými, které odkazují na přidělené objekty. Zpracování výjimek poskytuje strukturovaný a rozšiřitelný přístup k detekci a obnovení chyb. Výrazy lambda podporují techniky funkčního programování. Syntaxe LINQ (Language Integrated Query) vytváří společný vzor pro práci s daty z libovolného zdroje. Jazyková podpora asynchronních operací poskytuje syntaxi pro vytváření distribuovaných systémů. Jazyk C# má jednotný systém typů. Všechny typy jazyka C#, včetně primitivních typů, jako int jsou doublea , dědí z jednoho kořenového object typu. Všechny typy sdílejí sadu běžných operací. Hodnoty libovolného typu lze ukládat, převozovat a pracovat s hodnotami konzistentním způsobem. Jazyk C# navíc podporuje jak typy odkazů definované uživatelem , tak typy hodnot. Jazyk C# umožňuje dynamické přidělování objektů a v řádku úložiště zjednodušených struktur. Jazyk C# podporuje obecné metody a typy, které poskytují vyšší bezpečnost typů a výkon. Jazyk C# poskytuje iterátory, které implementátorům tříd kolekcí umožňují definovat vlastní chování pro klientský kód.

Jazyk C# klade důraz na verzi , aby se programy a knihovny v průběhu času vyvíjely kompatibilním způsobem. Aspekty návrhu jazyka C#virtualoverride, které byly přímo ovlivněny aspekty verzí, zahrnují samostatné modifikátory a , pravidla pro řešení přetížení metody a podporu explicitních deklarací členů rozhraní.

Architektura .NET

Programy v jazyce C# běží na .NET, virtuálním systému spouštění s názvem Common Language Runtime (CLR) a sadě knihoven tříd. CLR je implementace společné jazykové infrastruktury (CLI), která je mezinárodní standard od Microsoftu. Rozhraní příkazového řádku je základem pro vytváření prostředí pro spouštění a vývoj, ve kterých jazyky a knihovny bezproblémově spolupracují.

Zdrojový kód napsaný v jazyce C# je zkompilován do přechodného jazyka (IL), který odpovídá specifikaci rozhraní příkazového řádku. Kód a prostředky jazyka IL, jako jsou rastrové obrázky a řetězce, jsou uloženy v sestavení, obvykle s příponou .dll. Sestavení obsahuje manifest, který poskytuje informace o typech, verzi a jazykové verzi sestavení.

Při spuštění programu jazyka C# je sestavení načteno do modulu CLR. Modul CLR provádí kompilaci JIT (Just-In-Time), aby převádí kód IL na nativní instrukce počítače. Modul CLR poskytuje další služby související s automatickým uvolňováním paměti, zpracováním výjimek a s právy pro správu prostředků. Kód, který je spuštěn clr, se někdy označuje jako "spravovaný kód". "Nespravovaný kód" je zkompilován do nativního strojového jazyka, který cílí na konkrétní platformu.

Interoperabilita jazyků je klíčovou funkcí rozhraní .NET. Kód IL vytvořený kompilátorem jazyka C# odpovídá specifikaci CTS (Common Type Specification). Kód IL vygenerovaný z jazyka C# může pracovat s kódem, který byl vygenerován z verzí .NET jazyka F#, Visual Basic, C++. Existuje více než 20 dalších jazyků kompatibilních se standardem CTS. Jedno sestavení může obsahovat více modulů napsaných v různých jazycích .NET. Typy mohou odkazovat na sebe navzájem, jako by byly napsány ve stejném jazyce.

Kromě služeb za běhu obsahuje .NET také rozsáhlé knihovny. Tyto knihovny podporují mnoho různých úloh. Jsou uspořádané do oborů názvů, které poskytují širokou škálu užitečných funkcí. Knihovny zahrnují vše od vstupu souboru a výstupu až po manipulaci s řetězci po analýzu XML až po architektury webových aplikací až Windows Forms. Typická aplikace jazyka C# ve velké části využívá knihovnu tříd .NET ke zpracování běžných "instalačně".

Další informace o rozhraní .NET najdete v tématu Přehled rozhraní .NET.

Hello World

Program "Hello, World" se tradičně používá k zavedení programovacího jazyka. Tady je v jazyce C#:

C#

using System;class Hello
{
    static void Main()
    {
        Console.WriteLine("Hello, World");
    }
}

Program "Hello, World" začíná direktivou using , která odkazuje na obor System názvů . Obory názvů poskytují hierarchický způsob uspořádání programů a knihoven jazyka C#. Obory názvů obsahují typy a další obory názvů – System například obor názvů obsahuje řadu typů, Console například třídu odkazovanou v programu a řadu dalších oborů názvů, například IOCollectionsa . Direktiva using , která odkazuje na daný obor názvů, umožňuje nekvalifikované použití typů, které jsou členy daného oboru názvů. Kvůli direktivě using může program použít jako Console.WriteLine zkratku pro System.Console.WriteLine.

Třída Hello deklarovaná programem "Hello, World" má jeden člen, metodu s názvem Main. Metoda Main je deklarována pomocí static modifikátoru . Zatímco metody instance mohou odkazovat na konkrétní ohraničující instanci objektu thispomocí klíčového slova , statické metody pracují bez odkazu na konkrétní objekt. Podle konvence slouží statická metoda s Main názvem jako vstupní bod programu v jazyce C#.

Výstup programu je vytvořen metodou WriteLine třídy Console v oboru System názvů . Tato třída je poskytována standardními knihovnami tříd, na které ve výchozím nastavení kompilátor automaticky odkazuje.

Typy a proměnné

Typ definuje strukturu a chování všech dat v jazyce C#. Deklarace typu může obsahovat jeho členy, základní typ, rozhraní, která implementuje, a operace povolené pro tento typ. Proměnná je popisek, který odkazuje na instanci konkrétního typu.

V jazyce C# existují dva typy typů: typy hodnot a odkazové typy. Proměnné hodnotových typů přímo obsahují svá data. Proměnné odkazových typů ukládají odkazy na jejich data, druhá se označuje jako objekty. U odkazových typů je možné, aby dvě proměnné odkazovala na stejný objekt a aby operace s jednou proměnnou ovlivnily objekt, na který odkazuje druhá proměnná. U hodnotových typů mají proměnné svou vlastní kopii dat a není možné, aby operace na jedné ovlivnily ty druhé ( refout s výjimkou proměnných parametrů a ).

Identifikátor je název proměnné. Identifikátor je posloupnost znaků Unicode bez prázdných znaků. Identifikátorem může být vyhrazené slovo jazyka C#, pokud má předponu @. Použití vyhrazeného slova jako identifikátoru může být užitečné při interakci s jinými jazyky.

Typy hodnot jazyka C# jsou dále rozděleny na jednoduché typy, typy výčtu , typystruktur, typy hodnot s možnou hodnotou null a typy hodnot řazené kolekce členů. Odkazové typy jazyka C# jsou dále rozděleny na typy tříd, typy rozhranítypy polí a typy delegátů.

Následující osnova poskytuje přehled systému typů jazyka C#.

Týden 3

Deklarace typů

Programy v jazyce C# používají deklarace typů k vytváření nových typů. Deklarace typu Určuje název a členy nového typu. Šest kategorií typů v jazyce C# je uživatelsky definované: typy tříd, typy struktury, typy rozhraní, typy výčtu, typy delegátů a typy hodnot řazené kolekce členů. Můžete také deklarovat record typy, buď record struct nebo record class . Typy záznamů mají členy syntetizované kompilátorem. Záznamy slouží hlavně pro ukládání hodnot s minimálním přidruženým chováním.

  • classTyp definuje datovou strukturu obsahující datové členy (pole) a členy funkce (metody, vlastnosti a další). Typy tříd podporují jednu dědičnost a polymorfismus, mechanismy, které mohou odvozené třídy roztáhnout a specializovat základní třídy.
  • structTyp je podobný typu třídy v tom, že představuje strukturu s datovými členy a členy funkce. Nicméně na rozdíl od tříd, struktury jsou typy hodnot a obvykle nevyžadují přidělení haldy. Typy struktury nepodporují uživatelem zadanou dědičnost a všechny typy struktury implicitně dědí z typu object .
  • interfaceTyp definuje kontrakt jako pojmenovanou sadu veřejných členů. classNebo struct , který implementuje interface , musí poskytnout implementace členů rozhraní. interfaceMůže dědit z více základních rozhraní a class nebo struct může implementovat více rozhraní.
  • delegateTyp představuje odkazy na metody s konkrétním seznamem parametrů a návratovým typem. Delegáti umožňují zacházet s metodami jako s entitami, které lze přiřadit proměnným a předávat jako parametry. Delegáti jsou analogické jako typy funkcí poskytované funkčními jazyky. Jsou také podobné konceptu ukazatelů funkcí nalezených v některých jiných jazycích. Na rozdíl od ukazatelů na funkce jsou delegáti objektově orientovaný a typově bezpečný.

classTypy, struct , interface a delegate podporují obecné typy, na jejichž základě lze parametry používat s jinými typy.

Jazyk C# podporuje jednorozměrné a multidimenzionální pole libovolného typu. Na rozdíl od typů uvedených výše nemusí být typy polí deklarovány dříve, než mohou být použity. Místo toho jsou typy polí konstruovány pomocí názvu typu s hranatými závorkami. Například int[] je int jednorozměrné pole, int[,] je dvourozměrné pole int , a int[][] je jednorozměrné pole jednorozměrného pole nebo "vícenásobné" pole z int ...

Typy s možnou hodnotou null nevyžadují samostatnou definici. Pro každý typ T , který nepovoluje hodnotu null, existuje odpovídající typ T? s možnou hodnotou null, který může obsahovat další hodnotu, null . int?Například je typ, který může obsahovat libovolné 32 celé číslo nebo hodnotu null a string? je typ, který může obsahovat libovolnou string hodnotu null nebo.

Systém typů jazyka C# je sjednocením, aby hodnota libovolného typu mohla být považována object za. Každý typ v jazyce C# přímo nebo nepřímo je odvozen z object typu třídy a object je nejvyšší základní třídou všech typů. Hodnoty typů odkazů se považují za objekty pouhým zobrazením hodnot jako typu object . Hodnoty typů hodnot se považují za objekty prováděním operací zabalení a rozbalení. V následujícím příkladu int je hodnota převedena na object a zpět na int .


int i = 123;
object o = i;    // Boxing
int j = (int)o;  // Unboxing

Při přiřazení hodnoty typu hodnoty k object odkazu je "pole" přiděleno pro uchování hodnoty. Toto pole je instancí typu odkazu a hodnota je zkopírována do tohoto pole. Naopak, pokud object je odkaz přetypování na typ hodnoty, je provedena kontrolu, že odkazovaná object je pole správného typu hodnoty. Pokud je ověření úspěšné, je hodnota v poli zkopírována na typ hodnoty.

Sjednocený Typový systém v jazyce C# znamená, že typy hodnot jsou považovány object za odkazy na vyžádání. Z důvodu sjednocení, knihovny pro obecné účely, které používají typ object , lze použít se všemi typy odvozenými z object , včetně typů odkazů a typů hodnot.

V jazyce C# existuje několik druhů proměnných , včetně polí, prvků pole, místních proměnných a parametrů. Proměnné reprezentují umístění úložiště. Každá proměnná má typ, který určuje, jaké hodnoty mohou být uloženy v proměnné, jak je znázorněno níže.

  • Typ hodnoty, která není null
    • Hodnota, která má přesný typ
  • Typ hodnoty s možnou hodnotou null
    • nullHodnota nebo hodnota daného přesného typu
  • object
    • nullOdkaz, odkaz na objekt libovolného typu odkazu, nebo odkaz na zabalenou hodnotu libovolného typu hodnoty
  • Typ třídy
    • nullOdkaz, odkaz na instanci tohoto typu třídy nebo odkaz na instanci třídy odvozené z tohoto typu třídy
  • Typ rozhraní
    • nullOdkaz, odkaz na instanci typu třídy, která implementuje tento typ rozhraní, nebo odkaz na zabalenou hodnotu typu hodnoty, který implementuje tento typ rozhraní
  • Typ pole
    • nullOdkaz, odkaz na instanci tohoto typu pole nebo odkaz na instanci kompatibilního typu pole
  • Typ delegáta
    • nullOdkaz nebo odkaz na instanci kompatibilního typu delegáta

Týden 4

Struktura programu

Klíčovou organizační koncepcí v jazyce C# jsou programyobory názvůtypyčlenysestavení. Programy deklarují typy, které obsahují členy a mohou být uspořádány do oborů názvů. Příklady typů jsou třídy, struktury a rozhraní. Příklady členů jsou pole, metody, vlastnosti a události. Když jsou programy C# kompilovány, jsou fyzicky zabaleny do sestavení. Sestavení mají obvykle příponu .exe souboru nebo .dll v závislosti na tom, zda implementují .exe nebo knihovny, v uvedeném pořadí.

Jako malý příklad zvažte sestavení, které obsahuje následující kód:



{
    Entry _top;    public void Push(T data)
    {
        _top = new Entry(_top, data);
    }    public T Pop()
    {
        if (_top == null)
        {
            throw new InvalidOperationException();
        }
        T result = _top.Data;
        _top = _top.Next;        return result;
    }    class Entry
    {
        public Entry Next { get; set; }
        public T Data { get; set; }        public Entry(Entry next, T data)
        {
            Next = next;
            Data = data;
        }
    }
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">namespace Acme.Collections;public class Stack<T>
{
    Entry _top;    public void Push(T data)
    {
        _top = new Entry(_top, data);
    }    public T Pop()
    {
        if (_top == null)
        {
            throw new InvalidOperationException();
        }
        T result = _top.Data;
        _top = _top.Next;        return result;
    }    class Entry
    {
        public Entry Next { get; set; }
        public T Data { get; set; }        public Entry(Entry next, T data)
        {
            Next = next;
            Data = data;
        }
    }
}

Plně kvalifikovaný název této třídy je Acme.Collections.Stack . Třída obsahuje několik členů: pole s názvem _top , dvě metody pojmenované Push a Pop a vnořená třída s názvem Entry . EntryTřída dále obsahuje tři členy: vlastnost s názvem Next , vlastnost s názvem Data a konstruktor. StackJe Stack třída. Má jeden parametr typu, který je nahrazen konkrétním typem, T když je použit.

Zásobník je kolekce "first in-last-out" (filo). Do horní části zásobníku jsou přidány nové prvky. Když je prvek odebrán, je odebrán z horní části zásobníku. Předchozí příklad deklaruje Stack typ, který definuje úložiště a chování pro zásobník. Můžete deklarovat proměnnou, která odkazuje na instanci Stack typu pro použití této funkce.

Sestavení obsahují spustitelný kód ve formě instrukcí pro převodní jazyk (IL) a symbolické informace ve formě metadat. Předtím, než se spustí, kompilátor JIT (just-in-time) prostředí .NET Common Language Runtime převede kód IL v sestavení na kód specifický pro procesor.

Vzhledem k tomu, že sestavení je samo-popisující jednotku funkcí obsahující kód i metadata, není nutné #include direktivy a hlavičkové soubory v jazyce C#. Veřejné typy a členy obsažené v konkrétním sestavení jsou zpřístupněny v programu v jazyce C# pouhým odkazováním na toto sestavení při kompilování programu. Například tento program používá Acme.Collections.Stack třídu ze acme.dll sestavení:


();
        s.Push(1); // stack contains 1
        s.Push(10); // stack contains 1, 10
        s.Push(100); // stack contains 1, 10, 100
        Console.WriteLine(s.Pop()); // stack contains 1, 10
        Console.WriteLine(s.Pop()); // stack contains 1
        Console.WriteLine(s.Pop()); // stack is empty
    }
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">class Example
{
    public static void Main()
    {
        var s = new Acme.Collections.Stack<int>();
        s.Push(1); // stack contains 1
        s.Push(10); // stack contains 1, 10
        s.Push(100); // stack contains 1, 10, 100
        Console.WriteLine(s.Pop()); // stack contains 1, 10
        Console.WriteLine(s.Pop()); // stack contains 1
        Console.WriteLine(s.Pop()); // stack is empty
    }
}

Pro zkompilování tohoto programu byste se museli odkazovat na sestavení obsahující třídu zásobníku definovanou v předchozím příkladu.

Programy v jazyce C# mohou být uloženy v několika zdrojových souborech. Při kompilaci programu v jazyce C# jsou všechny zdrojové soubory zpracovávány společně a zdrojové soubory mohou volně odkazovat na sebe. V koncepčním případě je to, že všechny zdrojové soubory byly před zpracováním sloučeny do jednoho velkého souboru. Předávací deklarace nejsou v jazyce C# nikdy potřeba, protože s malým počtem výjimek je pořadí deklarací nedůležité. C# neomezuje zdrojový soubor na deklaraci pouze jednoho veřejného typu, ani nevyžaduje název zdrojového souboru, aby odpovídal typu deklarovanému ve zdrojovém souboru.

Týden 5

Organizační bloky, třídy a objekty

Třídy jsou nejzákladnějšími typy jazyka C#. Třída je datová struktura, která kombinuje stav (pole) a akce (metody a další členy funkce) v jedné jednotce. Třída poskytuje definici pro instance třídy, označované také jako objekty. Třídy podporují dědičnost a polymorfismus, mechanismy, kterými mohou odvozené třídy rozšířit a specializovat základní třídy.

Nové třídy se vytvářejí pomocí deklarací tříd. Deklarace třídy začíná záhlavím. Hlavička určuje:

  • Atributy a modifikátory třídy
  • Název třídy
  • Základní třída (při dědění ze základní třídy)
  • Rozhraní implementované třídou.

Za hlavičkou následuje tělo třídy, které se skládá ze seznamu deklarací členů zapsaných mezi oddělovači { a }.

Následující kód zobrazuje deklaraci jednoduché třídy s názvem Point:


 (X, Y) = (x, y);
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public class Point
{
    public int X { get; }
    public int Y { get; }
    
    public Point(int x, int y) => (X, Y) = (x, y);
}

Instance tříd jsou vytvořeny pomocí operátoru new , který přiděluje paměť nové instanci, vyvolá konstruktor pro inicializaci instance a vrátí odkaz na instanci. Následující příkazy vytvoří dva Point objekty a uloží odkazy na tyto objekty ve dvou proměnných:


var p1 = new Point(0, 0);
var p2 = new Point(10, 20);

Paměť zabíraná objektem je automaticky uvolněna, když objekt již není dostupný. Není nutné ani není možné explicitně uvolnit objekty v jazyce C#.


var p1 = new Point(0, 0);
var p2 = new Point(10, 20);

Aplikace nebo testy pro algoritmy mohou vyžadovat vytvoření více Point objektů. Následující třída vygeneruje sekvenci náhodných bodů. Počet bodů je nastaven primárním parametrem konstruktoru . Primární parametr numPoints konstruktoru je v oboru pro všechny členy třídy:


 CreatePoints()
    {
        var generator = new Random();
        for (int i = 0; i < numberOfPoints; i++)
        {
            yield return new Point(generator.Next(), generator.Next());
        }
    }
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public class PointFactory(int numberOfPoints)
{
    public IEnumerable<Point> CreatePoints()
    {
        var generator = new Random();
        for (int i = 0; i < numberOfPoints; i++)
        {
            yield return new Point(generator.Next(), generator.Next());
        }
    }
}

Můžete použít třídu, jak je znázorněno v následujícím kódu:


var factory = new PointFactory(10);
foreach (var point in factory.CreatePoints())
{
    Console.WriteLine($"({point.X}, {point.Y})");
}

Parametry typu

Obecné třídy definují parametry typu. Parametry typu jsou seznam názvů parametrů typu uzavřených v šikmých závorkách. Parametry typu se řídí názvem třídy. Parametry typu pak lze použít v těle deklarací třídy k definování členů třídy. V následujícím příkladu jsou TFirst parametry Pair typu a TSecond:



{
    public TFirst First { get; }
    public TSecond Second { get; }
    
    public Pair(TFirst first, TSecond second) => 
        (First, Second) = (first, second);
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public class Pair<TFirst, TSecond>
{
    public TFirst First { get; }
    public TSecond Second { get; }
    
    public Pair(TFirst first, TSecond second) => 
        (First, Second) = (first, second);
}

Typ třídy, který je deklarován k převzetí parametrů typu, se nazývá obecný typ třídy. Typy struktur, rozhraní a delegátů můžou být také obecné. Při použití obecné třídy musí být pro každý z parametrů typu zadány argumenty typu:


(1, "two");
int i = pair.First;     //TFirst int
string s = pair.Second; //TSecond string
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">var pair = new Pair<int, string>(1, "two");
int i = pair.First;     //TFirst int
string s = pair.Second; //TSecond string

Obecný typ se zadanými argumenty typu, jako Pair<int,string> je výše, se nazývá konstruovaný typ.

Základní třídy

Deklarace třídy může určit základní třídu. Postupujte podle parametrů názvu třídy a zadejte dvojtečku a název základní třídy. Vynechání specifikace základní třídy je stejné jako odvození z typu object. V následujícím příkladu je Pointzákladní třída .Point3D Z prvního příkladu je objectzákladní třída :Point


public class Point3D : Point
{
    public int Z { get; set; }
    
    public Point3D(int x, int y, int z) : base(x, y)
    {
        Z = z;
    }
}

Třída dědí členy své základní třídy. Dědičnost znamená, že třída implicitně obsahuje téměř všechny členy své základní třídy. Třída nedědí instance a statické konstruktory a finalizátor. Odvozená třída může přidat nové členy k členům, které dědí, ale nemůže odebrat definici zděděného člena. V předchozím příkladu Point3D dědí X členy a Y z Pointa každá Point3D instance obsahuje tři vlastnosti, XYZ.

Existuje implicitní převod z typu třídy na kterýkoli z jejích základních typů tříd. Proměnná typu třídy může odkazovat na instanci této třídy nebo na instanci jakékoli odvozené třídy. Například u předchozích deklarací třídy může proměnná typu Point odkazovat na nebo PointPoint3Dna :


Point a = new(10, 20);
Point b = new Point3D(10, 20, 30);

Struktury

Třídy definují typy, které podporují dědičnost a polymorfismus. Umožňují vytvářet sofistikované chování založené na hierarchiích odvozených tříd. Naproti tomu typy struktur jsou jednodušší typy, jejichž primárním účelem je ukládání datových hodnot. Struktury nemohou deklarovat základní typ; implicitně odvozují od System.ValueType. Z typu nemůžete odvodit jiné struct typy struct . Jsou implicitně zapečetěné.


 (X, Y) = (x, y);
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public struct Point
{
    public double X { get; }
    public double Y { get; }
    
    public Point(double x, double y) => (X, Y) = (x, y);
}

Týden 6

Rozhraní

Rozhraní definuje kontrakt, který lze implementovat pomocí tříd a struktur. Definujete rozhraní pro deklaraci schopností, které jsou sdíleny mezi různými typy. Rozhraní například definuje konzistentní způsob procházení System.Collections.Generic.IEnumerable<T> všech položek v kolekci, například pole. Rozhraní může obsahovat metody, vlastnosti, události a indexery. Rozhraní obvykle neposkytuje implementace členů, které definuje – pouze určuje členy, které musí být dodány třídami nebo strukturami, které implementují rozhraní.

Rozhraní mohou využívat více dědičnosti. V následujícím příkladu rozhraní IComboBox dědí z obou ITextBox a IListBox.


interface IControl
{
    void Paint();
}interface ITextBox : IControl
{
    void SetText(string text);
}interface IListBox : IControl
{
    void SetItems(string[] items);
}interface IComboBox : ITextBox, IListBox { }

Třídy a struktury mohou implementovat více rozhraní. V následujícím příkladu třída EditBox implementuje a IControlIDataBound.


interface IDataBound
{
    void Bind(Binder b);
}public class EditBox : IControl, IDataBound
{
    public void Paint() { }
    public void Bind(Binder b) { }
}

Když třída nebo struktura implementuje konkrétní rozhraní, instance této třídy nebo struktury lze implicitně převést na tento typ rozhraní. Například


EditBox editBox = new();
IControl control = editBox;
IDataBound dataBound = editBox;

Výčty

Typ výčtu definuje sadu konstantních hodnot. Následující enum kód deklaruje konstanty, které definují různé kořenové zeleniny:


public enum SomeRootVegetable
{
    HorseRadish,
    Radish,
    Turnip
}

Můžete také definovat, který enum se má použít v kombinaci jako příznaky. Následující deklarace deklaruje sadu vlajek pro čtyři roční období. Může být použita libovolná kombinace ročních období, včetně All hodnoty, která zahrnuje všechna roční období:


[Flags]
public enum Seasons
{
    None = 0,
    Summer = 1,
    Autumn = 2,
    Winter = 4,
    Spring = 8,
    All = Summer | Autumn | Winter | Spring
}

Následující příklad ukazuje deklarace obou předchozích výčtů:


var turnip = SomeRootVegetable.Turnip;var spring = Seasons.Spring;
var startingOnEquinox = Seasons.Spring | Seasons.Autumn;
var theYear = Seasons.All;

Typy nullable

Proměnné libovolného typu mohou být deklarovány jako nenulové nebo s možnou hodnotou null. Proměnná s možnou hodnotou null může obsahovat další null hodnotu, která neudávají žádnou hodnotu. Typy hodnot s možnou hodnotou null (struktury nebo výčty) jsou reprezentovány hodnotou System.Nullable<T>. Referenční typy s možnou hodnotou null i odkaz s možnou hodnotou null jsou reprezentovány základním referenčním typem. Rozdíl je reprezentován metadaty přečtené kompilátorem a některými knihovnami. Kompilátor zobrazí upozornění při dereferencování odkazů s možnou hodnotou null, aniž by nejprve zkontroloval jejich hodnotu proti null. Kompilátor také poskytuje upozornění, když je odkazům, které nemají hodnotu null, přiřazena hodnota, která může být null. Následující příklad deklaruje int s možnou hodnotou null a inicializuje ho na null. Pak nastaví hodnotu na 5. Ukazuje stejný koncept s řetězcem s možnou hodnotou null. Další informace najdete v tématech typy hodnot s možnou hodnotou null a odkazové typy s možnou hodnotou null.


int? optionalInt = default; 
optionalInt = 5;
string? optionalText = default;
optionalText = "Hello World.";

N-tice

C# podporuje řazené kolekce členů, které poskytují stručnou syntaxi pro seskupení více datových prvků v jednoduché datové struktuře. Vytvoření instance řazené kolekce členů deklarací typů a názvů členů mezi ( a ), jak je znázorněno v následujícím příkladu:


(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
//Output:
//Sum of 3 elements is 4.5.

Týden 7

Členové

Členy objektu class jsou buď statické členy , nebo členy instance. Statické členy patří do tříd a členy instance patří k objektům (instancím tříd).

Následující seznam obsahuje přehled typů členů, které může třída obsahovat.

  • Konstanty: Konstantní hodnoty přidružené ke třídě
  • Pole: Proměnné, které jsou přidruženy k třídě
  • Metody: Akce, které může třída provádět
  • Vlastnosti: Akce spojené se čtením a zápisem pojmenovaných vlastností třídy
  • Indexery: Akce přidružené k indexování instancí třídy, jako je pole
  • Události: Oznámení, která mohou být generována třídou
  • Operátory: Převody a operátory výrazů podporované třídou
  • Konstruktory: Akce potřebné k inicializaci instancí třídy nebo samotné třídy
  • Finalizační metody: Akce provedené před trvalým zahozením instancí třídy
  • Typy: Vnořené typy deklarované třídou

Usnadnění

Každý člen třídy má přidruženou přístupnost, která řídí oblasti textu programu, které mají přístup k členu. Existuje šest možných forem přístupnosti. Níže jsou shrnuté modifikátory přístupu.

  • public: Přístup není omezený.
  • private: Přístup je omezen na tuto třídu.
  • protected: Přístup je omezen na tuto třídu nebo třídy odvozené z této třídy.
  • internal: Přístup je omezen na aktuální sestavení (.exe nebo .dll).
  • protected internal: Přístup je omezen na tuto třídu, třídy odvozené z této třídy nebo třídy v rámci stejného sestavení.
  • private protected: Přístup je omezen na tuto třídu nebo třídy odvozené z tohoto typu v rámci stejného sestavení.

Pole

Pole je proměnná, která je přidružená ke třídě nebo instanci třídy.

Pole deklarované se statickým modifikátorem definuje statické pole. Statické pole identifikuje přesně jedno umístění úložiště. Bez ohledu na to, kolik instancí třídy je vytvořeno, existuje vždy jen jedna kopie statického pole.

Pole deklarované bez statického modifikátoru definuje pole instance. Každá instance třídy obsahuje samostatnou kopii všech polí instance této třídy.

V následujícím příkladu Color má každá instance třídy samostatnou kopii Rpolí , GB instance, ale existuje pouze jedna kopie statických Blackpolí , WhiteRedGreenBlue :


public class Color
{
    public static readonly Color Black = new(0, 0, 0);
    public static readonly Color White = new(255, 255, 255);
    public static readonly Color Red = new(255, 0, 0);
    public static readonly Color Green = new(0, 255, 0);
    public static readonly Color Blue = new(0, 0, 255);
    
    public byte R;
    public byte G;
    public byte B;    public Color(byte r, byte g, byte b)
    {
        R = r;
        G = g;
        B = b;
    }
}

Jak je znázorněno v předchozím příkladu, pole jen pro čtení mohou být deklarována pomocí modifikátoru readonly . Přiřazení k poli jen pro čtení může probíhat pouze jako součást deklarace pole nebo v konstruktoru ve stejné třídě.

Metody

Metoda je člen, který implementuje výpočet nebo akci, která může být provedena objektem nebo třídou. Statické metody jsou přístupné prostřednictvím třídy. K metodám instance se přistupuje prostřednictvím instancí třídy.

Metody mohou mít seznam parametrů, které představují hodnoty nebo odkazy na proměnné předávané metodě. Metody mají návratový typ, který určuje typ hodnoty vypočítané a vrácené metodou. Návratový typ metody je void ten, který nevrací hodnotu.

Podobně jako typy mohou mít i metody sadu parametrů typu, pro které musí být zadány argumenty typu při zavolání metody. Na rozdíl od typů mohou být argumenty typu často odvozeny z argumentů volání metody a nemusí být explicitně uvedeny.

Podpis metody musí být jedinečný ve třídě, ve které je metoda deklarována. Podpis metody se skládá z názvu metody, počtu parametrů typu a čísla, modifikátorů a typů jejích parametrů. Podpis metody neobsahuje návratový typ.

Pokud je tělo metody jedním výrazem, lze metodu definovat pomocí kompaktního formátu výrazu, jak je znázorněno v následujícím příkladu:


 "This is an object";
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public override string ToString() => "This is an object";

Parametry

Parametry se používají k předávání hodnot nebo odkazů na proměnné metodám. Parametry metody získávají své skutečné hodnoty z argumentů , které jsou zadány při vyvolání metody. Existují čtyři druhy parametrů: parametry hodnot, referenční parametry, výstupní parametry a pole parametrů.

Parametr hodnoty se používá pro předávání vstupních argumentů. Parametr hodnoty odpovídá místní proměnné, která získá počáteční hodnotu z argumentu, který byl předán pro parametr. Úpravy parametru hodnoty nemají vliv na argument, který byl předán parametru.

Parametry hodnoty mohou být volitelné zadáním výchozí hodnoty, aby bylo možné vynechat odpovídající argumenty.

Parametr odkazu se používá pro předávání argumentů podle odkazu. Argument předaný pro parametr odkazu musí být proměnná s určitou hodnotou. Během provádění metody představuje parametr reference stejné umístění úložiště jako proměnná argumentu. Referenční parametr je deklarován pomocí modifikátoru ref . Následující příklad ukazuje použití ref parametrů.


static void Swap(ref int x, ref int y)
{
    int temp = x;
    x = y;
    y = temp;
}public static void SwapExample()
{
    int i = 1, j = 2;
    Swap(ref i, ref j);
    Console.WriteLine($"{i} {j}");    // "2 1"
}

Výstupní parametr se používá pro předávání argumentů podle odkazu. Podobá se referenčnímu parametru s tím rozdílem, že nevyžaduje explicitní přiřazení hodnoty k argumentu poskytnutému volajícím. Výstupní parametr je deklarován pomocí modifikátoru out . Následující příklad ukazuje použití out parametrů.


static void Divide(int x, int y, out int quotient, out int remainder)
{
    quotient = x / y;
    remainder = x % y;
}public static void OutUsage()
{
    Divide(10, 3, out int quo, out int rem);
    Console.WriteLine($"{quo} {rem}");	// "3 1"
}

Pole parametrů umožňuje předat metodě proměnný počet argumentů. Pole parametrů je deklarováno pomocí modifikátoru params . Pole parametrů může být pouze posledním parametrem metody a typ pole parametrů musí být jednorozměrný typ pole. Metody WriteSystem.Console a WriteLine třídy jsou dobrými příklady použití pole parametrů. Jsou deklarovány následujícím způsobem.


public class Console
{
    public static void Write(string fmt, params object[] args) { }
    public static void WriteLine(string fmt, params object[] args) { }
    // ...
}

V metodě, která používá pole parametrů, se pole parametrů chová přesně jako běžný parametr typu pole. Při vyvolání metody s polem parametrů je však možné předat buď jeden argument typu pole parametru, nebo libovolný počet argumentů typu prvku pole parametru. V druhém případě se instance pole automaticky vytvoří a inicializuje pomocí zadaných argumentů. Tento příklad


int x, y, z;
x = 3;
y = 4;
z = 5;
Console.WriteLine("x={0} y={1} z={2}", x, y, z);

je ekvivalentem psaní následujícího textu.


int x = 3, y = 4, z = 5;string s = "x={0} y={1} z={2}";
object[] args = new object[3];
args[0] = x;
args[1] = y;
args[2] = z;
Console.WriteLine(s, args);

Tělo metody a místní proměnné

Tělo metody určuje příkazy, které se mají provést při vyvolání metody.

Tělo metody může deklarovat proměnné, které jsou specifické pro vyvolání metody. Takové proměnné se nazývají místní proměnné. Deklarace místní proměnné určuje název typu, název proměnné a případně počáteční hodnotu. Následující příklad deklaruje místní proměnnou i s počáteční hodnotou nula a místní proměnnou j bez počáteční hodnoty.


class Squares
{
    public static void WriteSquares()
    {
        int i = 0;
        int j;
        while (i < 10)
        {
            j = i * i;
            Console.WriteLine($"{i} x {i} = {j}");
            i++;
        }
    }
}

Jazyk C# vyžaduje, aby před získáním její hodnoty byla jednoznačně přiřazena místní proměnná. Pokud například deklarace předchozí i neobsahuje počáteční hodnotu, kompilátor by ohlásil chybu pro pozdější použití nástroje i , protože i by v těchto bodech v programu nebyla jednoznačně přiřazena.

Metoda může pomocí return příkazů vrátit řízení volajícímu. V metodě vracející voidpříkazy return nemůžou zadat výraz. V metodě vracející non-void return musí příkazy obsahovat výraz, který vypočítá návratovou hodnotu.

Týden 8

Statické metody a metody instancí

Metoda deklarovaná modifikátorem static je statická metoda. Statická metoda nepracuje s konkrétní instancí a má přímý přístup pouze k statickým členům.

Metoda deklarovaná bez modifikátoru static je metoda instance. Metoda instance pracuje s konkrétní instancí a má přístup k statickým i členům instance. K instanci, na které byla vyvolána metoda instance, lze explicitně přistupovat jako this. Jedná se o chybu, na this které se odkazuje ve statické metodě.

Následující Entity třída má statické členy i členy instance.


class Entity
{
    static int s_nextSerialNo;
    int _serialNo;
    
    public Entity()
    {
        _serialNo = s_nextSerialNo++;
    }
    
    public int GetSerialNo()
    {
        return _serialNo;
    }
    
    public static int GetNextSerialNo()
    {
        return s_nextSerialNo;
    }
    
    public static void SetNextSerialNo(int value)
    {
        s_nextSerialNo = value;
    }
}

Každá Entity instance obsahuje sériové číslo (a pravděpodobně i některé další informace, které tady nejsou uvedené). Konstruktor Entity (který je jako metoda instance) inicializuje novou instanci s dalším dostupným sériovým číslem. Vzhledem k tomu, že konstruktor je členem instance, je povolen přístup k poli instance i _serialNo statickému s_nextSerialNo poli.

GetNextSerialNo Statické metody a SetNextSerialNo mají přístup ke statickému s_nextSerialNo poli, ale při přímém přístupu _serialNo k poli instance by došlo k chybě.

Následující příklad ukazuje použití Entity třídy .


Entity.SetNextSerialNo(1000);
Entity e1 = new();
Entity e2 = new();
Console.WriteLine(e1.GetSerialNo());          // Outputs "1000"
Console.WriteLine(e2.GetSerialNo());          // Outputs "1001"
Console.WriteLine(Entity.GetNextSerialNo());  // Outputs "1002"

Statické SetNextSerialNo metody a GetNextSerialNo jsou vyvolány ve třídě, zatímco GetSerialNo metoda instance je vyvolána na instancích třídy .

Virtuální, přepsání a abstraktní metody

Pomocí virtuálních, přepsání a abstraktních metod můžete definovat chování pro hierarchii typů tříd. Vzhledem k tomu, že třída může být odvozena ze základní třídy, mohou tyto odvozené třídy potřebovat změnit chování implementované v základní třídě. Virtuální metoda je metoda deklarovaná a implementovaná v základní třídě, kde každá odvozená třída může poskytovat konkrétnější implementaci. Metoda override je metoda implementovaná v odvozené třídě, která upravuje chování implementace základní třídy. Abstraktní metoda je metoda deklarovaná v základní třídě, která musí být přepsána ve všech odvozených třídách. Abstraktní metody ve skutečnosti nedefinuje implementaci v základní třídě.

Volání metod instance se může přeložit na implementaci základní třídy nebo odvozené třídy. Typ proměnné určuje její typ v době kompilaceTyp kompilace je typ, který kompilátor používá k určení svých členů. Proměnná však může být přiřazena instanci libovolného typu odvozeného z jejího typu kompilaceTyp runtime je typ skutečné instance, na které proměnná odkazuje.

Při vyvolání virtuální metody typ za běhu instance, pro kterou se toto vyvolání provádí, určuje skutečnou implementaci metody, která se má vyvolat. Při vyvolání nevirtuální metody je určujícím faktorem typ kompilace instance.

Virtuální metodu lze přepsat v odvozené třídě. Pokud deklarace metody instance obsahuje modifikátor přepsání, metoda přepíše zděděnou virtuální metodu se stejným podpisem. Deklarace virtuální metody zavádí novou metodu. Deklarace metody přepsání se specializuje na existující zděděnou virtuální metodu tím, že poskytuje novou implementaci této metody.

Abstraktní metoda je virtuální metoda bez implementace. Abstraktní metoda je deklarována s modifikátorem abstract a je povolena pouze v abstraktní třídě. Abstraktní metoda musí být přepsána v každé ne abstraktní odvozené třídě.

Následující příklad deklaruje abstraktní třídu , Expressionkterá představuje uzel stromu výrazů, a tři odvozené třídy, ConstantVariableReferenceOperation, které implementují uzly stromu výrazů pro konstanty, odkazy na proměnné a aritmetické operace. (Tento příklad je podobný typu, ale nesouvisí s typy stromu výrazů).


 vars);
}public class Constant : Expression
{
    double _value;
    
    public Constant(double value)
    {
        _value = value;
    }
    
    public override double Evaluate(Dictionary vars)
    {
        return _value;
    }
}public class VariableReference : Expression
{
    string _name;
    
    public VariableReference(string name)
    {
        _name = name;
    }
    
    public override double Evaluate(Dictionary vars)
    {
        object value = vars[_name] ?? throw new Exception($"Unknown variable: {_name}");
        return Convert.ToDouble(value);
    }
}public class Operation : Expression
{
    Expression _left;
    char _op;
    Expression _right;
    
    public Operation(Expression left, char op, Expression right)
    {
        _left = left;
        _op = op;
        _right = right;
    }
    
    public override double Evaluate(Dictionary vars)
    {
        double x = _left.Evaluate(vars);
        double y = _right.Evaluate(vars);
        switch (_op)
        {
            case '+': return x + y;
            case '-': return x - y;
            case '*': return x * y;
            case '/': return x / y;
            
            default: throw new Exception("Unknown operator");
        }
    }
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public abstract class Expression
{
    public abstract double Evaluate(Dictionary<string, object> vars);
}public class Constant : Expression
{
    double _value;
    
    public Constant(double value)
    {
        _value = value;
    }
    
    public override double Evaluate(Dictionary<string, object> vars)
    {
        return _value;
    }
}public class VariableReference : Expression
{
    string _name;
    
    public VariableReference(string name)
    {
        _name = name;
    }
    
    public override double Evaluate(Dictionary<string, object> vars)
    {
        object value = vars[_name] ?? throw new Exception($"Unknown variable: {_name}");
        return Convert.ToDouble(value);
    }
}public class Operation : Expression
{
    Expression _left;
    char _op;
    Expression _right;
    
    public Operation(Expression left, char op, Expression right)
    {
        _left = left;
        _op = op;
        _right = right;
    }
    
    public override double Evaluate(Dictionary<string, object> vars)
    {
        double x = _left.Evaluate(vars);
        double y = _right.Evaluate(vars);
        switch (_op)
        {
            case '+': return x + y;
            case '-': return x - y;
            case '*': return x * y;
            case '/': return x / y;
            
            default: throw new Exception("Unknown operator");
        }
    }
}

Předchozí čtyři třídy lze použít k modelování aritmetických výrazů. Například při použití instancí těchto tříd může být výraz x + 3 reprezentován následujícím způsobem.


Expression e = new Operation(
    new VariableReference("x"),
    '+',
    new Constant(3));

Metoda EvaluateExpression instance je vyvolána k vyhodnocení daného výrazu double a vytvoření hodnoty. Metoda přebírá Dictionary argument, který obsahuje názvy proměnných (jako klíče položek) a hodnoty (jako hodnoty položek). Vzhledem k tomu, že Evaluate je abstraktní metoda, musí ne abstraktní třídy odvozené z Expression přepisovat Evaluate.

Implementace Evaluate typu A Constantjednoduše vrátí uloženou konstantu. VariableReferenceImplementace objektu vyhledá název proměnné ve slovníku a vrátí výslednou hodnotu. Implementace Operationnejprve vyhodnotí levé a pravé operandy (rekurzivním vyvoláním jejich Evaluate metod) a pak provede danou aritmetickou operaci.

Následující program používá Expression třídy k vyhodnocení výrazu x * (y + 2) pro různé hodnoty x a y.


 vars = new();
vars["x"] = 3;
vars["y"] = 5;
Console.WriteLine(e.Evaluate(vars)); // "21"
vars["x"] = 1.5;
vars["y"] = 9;
Console.WriteLine(e.Evaluate(vars)); // "16.5"
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">Expression e = new Operation(
    new VariableReference("x"),
    '*',
    new Operation(
        new VariableReference("y"),
        '+',
        new Constant(2)
    )
);
Dictionary<string, object> vars = new();
vars["x"] = 3;
vars["y"] = 5;
Console.WriteLine(e.Evaluate(vars)); // "21"
vars["x"] = 1.5;
vars["y"] = 9;
Console.WriteLine(e.Evaluate(vars)); // "16.5"

Týden 9

Přetížení metody

Přetížení metody umožňuje více metodám ve stejné třídě mít stejný název, pokud mají jedinečné podpisy. Při kompilaci vyvolání přetížené metody kompilátor používá rozlišení přetížení k určení konkrétní metody k vyvolání. Řešení přetížení najde jednu metodu, která nejlépe odpovídá argumentu. Pokud se nenajde žádná nejlepší shoda, nahlásí se chyba. Následující příklad ukazuje řešení přetížení. Komentář pro každé volání v UsageExample metodě ukazuje, která metoda je vyvolána.


 Console.WriteLine("F()");
    static void F(object x) => Console.WriteLine("F(object)");
    static void F(int x) => Console.WriteLine("F(int)");
    static void F(double x) => Console.WriteLine("F(double)");
    static void F(T x) => Console.WriteLine($"F(T), T is {typeof(T)}");            
    static void F(double x, double y) => Console.WriteLine("F(double, double)");
    
    public static void UsageExample()
    {
        F();            // Invokes F()
        F(1);           // Invokes F(int)
        F(1.0);         // Invokes F(double)
        F("abc");       // Invokes F(T), T is System.String
        F((double)1);   // Invokes F(double)
        F((object)1);   // Invokes F(object)
        F(1);      // Invokes F(T), T is System.Int32
        F(1, 1);        // Invokes F(double, double)
    }
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">class OverloadingExample
{
    static void F() => Console.WriteLine("F()");
    static void F(object x) => Console.WriteLine("F(object)");
    static void F(int x) => Console.WriteLine("F(int)");
    static void F(double x) => Console.WriteLine("F(double)");
    static void F<T>(T x) => Console.WriteLine($"F<T>(T), T is {typeof(T)}");            
    static void F(double x, double y) => Console.WriteLine("F(double, double)");
    
    public static void UsageExample()
    {
        F();            // Invokes F()
        F(1);           // Invokes F(int)
        F(1.0);         // Invokes F(double)
        F("abc");       // Invokes F<T>(T), T is System.String
        F((double)1);   // Invokes F(double)
        F((object)1);   // Invokes F(object)
        F<int>(1);      // Invokes F<T>(T), T is System.Int32
        F(1, 1);        // Invokes F(double, double)
    }
}

Jak ukazuje příklad, konkrétní metodu lze vždy vybrat explicitním přetypováním argumentů na přesné typy parametrů a argumenty typu.

Další členy funkce

Členy, které obsahují spustitelný kód, se souhrnně označují jako členy funkce třídy. Předchozí část popisuje metody, které jsou primárními typy členů funkce. Tato část popisuje další druhy členů funkcí podporované jazykem C#: konstruktory, vlastnosti, indexery, události, operátory a finalizační metody.

Následující příklad ukazuje obecnou třídu s názvem MyList<T>, která implementuje rozšiřitelný seznam objektů. Třída obsahuje několik příkladů nejběžnějších druhů členů funkce.



{
    const int DefaultCapacity = 4;    T[] _items;
    int _count;    public MyList(int capacity = DefaultCapacity)
    {
        _items = new T[capacity];
    }    public int Count => _count;    public int Capacity
    {
        get =>  _items.Length;
        set
        {
            if (value < _count) value = _count;
            if (value != _items.Length)
            {
                T[] newItems = new T[value];
                Array.Copy(_items, 0, newItems, 0, _count);
                _items = newItems;
            }
        }
    }    public T this[int index]
    {
        get => _items[index];
        set
        {
            if (!object.Equals(_items[index], value)) {
                _items[index] = value;
                OnChanged();
            }
        }
    }    public void Add(T item)
    {
        if (_count == Capacity) Capacity = _count * 2;
        _items[_count] = item;
        _count++;
        OnChanged();
    }
    protected virtual void OnChanged() =>
        Changed?.Invoke(this, EventArgs.Empty);    public override bool Equals(object other) =>
        Equals(this, other as MyList);    static bool Equals(MyList a, MyList b)
    {
        if (Object.ReferenceEquals(a, null)) return Object.ReferenceEquals(b, null);
        if (Object.ReferenceEquals(b, null) || a._count != b._count)
            return false;
        for (int i = 0; i < a._count; i++)
        {
            if (!object.Equals(a._items[i], b._items[i]))
            {
                return false;
            }
        }
        return true;
    }    public event EventHandler Changed;    public static bool operator ==(MyList a, MyList b) =>
        Equals(a, b);    public static bool operator !=(MyList a, MyList b) =>
        !Equals(a, b);
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public class MyList<T>
{
    const int DefaultCapacity = 4;    T[] _items;
    int _count;    public MyList(int capacity = DefaultCapacity)
    {
        _items = new T[capacity];
    }    public int Count => _count;    public int Capacity
    {
        get =>  _items.Length;
        set
        {
            if (value < _count) value = _count;
            if (value != _items.Length)
            {
                T[] newItems = new T[value];
                Array.Copy(_items, 0, newItems, 0, _count);
                _items = newItems;
            }
        }
    }    public T this[int index]
    {
        get => _items[index];
        set
        {
            if (!object.Equals(_items[index], value)) {
                _items[index] = value;
                OnChanged();
            }
        }
    }    public void Add(T item)
    {
        if (_count == Capacity) Capacity = _count * 2;
        _items[_count] = item;
        _count++;
        OnChanged();
    }
    protected virtual void OnChanged() =>
        Changed?.Invoke(this, EventArgs.Empty);    public override bool Equals(object other) =>
        Equals(this, other as MyList<T>);    static bool Equals(MyList<T> a, MyList<T> b)
    {
        if (Object.ReferenceEquals(a, null)) return Object.ReferenceEquals(b, null);
        if (Object.ReferenceEquals(b, null) || a._count != b._count)
            return false;
        for (int i = 0; i < a._count; i++)
        {
            if (!object.Equals(a._items[i], b._items[i]))
            {
                return false;
            }
        }
        return true;
    }    public event EventHandler Changed;    public static bool operator ==(MyList<T> a, MyList<T> b) =>
        Equals(a, b);    public static bool operator !=(MyList<T> a, MyList<T> b) =>
        !Equals(a, b);
}

Konstruktory

Jazyk C# podporuje konstruktory instancí i statické konstruktory. Konstruktor instance je člen, který implementuje akce potřebné k inicializaci instance třídy. Statický konstruktor je člen, který implementuje akce potřebné k inicializaci samotné třídy při jejím prvním načtení.

Konstruktor je deklarován jako metoda bez návratového typu a se stejným názvem jako obsahující třída. Pokud deklarace konstruktoru static obsahuje modifikátor, deklaruje statický konstruktor. V opačném případě deklaruje konstruktor instance.

Konstruktory instance mohou být přetíženy a mohou mít volitelné parametry. MyList<T> Například třída deklaruje jeden konstruktor instance s jedním volitelným int parametrem. Konstruktory instancí jsou vyvolány pomocí operátoru new . Následující příkazy přidělují dvě MyList<string> instance pomocí konstruktoru MyList třídy s volitelným argumentem a bez něj.


 list1 = new();
MyList list2 = new(10);
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">MyList<string> list1 = new();
MyList<string> list2 = new(10);

Na rozdíl od jiných členů se konstruktory instancí nedědí. Třída nemá žádné konstruktory instance kromě těch, které jsou skutečně deklarovány ve třídě . Pokud není pro třídu zadán žádný konstruktor instance, je automaticky zadán prázdný konstruktor bez parametrů.

Vlastnosti

Vlastnosti jsou přirozeným rozšířením polí. Oba jsou pojmenované členy s přidruženými typy a syntaxe pro přístup k polím a vlastnostem je stejná. Na rozdíl od polí ale vlastnosti neoznamuje umístění úložiště. Místo toho mají vlastnosti přístupové objekty , které určují příkazy spuštěné při čtení nebo zápisu jejich hodnot. Hodnotu načte objekt get accessor . Objekt set accessor zapíše hodnotu .

Vlastnost je deklarována jako pole s tím rozdílem, že deklarace končí na get accessor nebo set accessor zapsaným mezi oddělovači { a } místo toho, aby končila středníkem. Vlastnost, která má přistupovací objekt get i objekt set, je vlastnost pro čtení i zápis. Vlastnost, která má pouze přístupový objekt get, je vlastnost jen pro čtení. Vlastnost, která má pouze objekt set, je vlastnost určená jen pro zápis.

Přístup get odpovídá metodě bez parametrů s návratovou hodnotou typu vlastnosti. Objekt set accessor odpovídá metodě s jedním parametrem s názvem value a bez návratového typu. Objekt get accessor vypočítá hodnotu vlastnosti . Objekt set accessor poskytuje novou hodnotu vlastnosti . Když je vlastnost cílem přiřazení nebo operandem ++ nebo --, je vyvolána přístupová položka sady. V jiných případech, kdy se odkazuje na vlastnost, se vyvolá přístup get.

Třída MyList<T> deklaruje dvě vlastnosti, Count a Capacity, které jsou jen pro čtení a pro čtení i zápis. Následující kód je příkladem použití těchto vlastností:


 names = new();
names.Capacity = 100;   // Invokes set accessor
int i = names.Count;    // Invokes get accessor
int j = names.Capacity; // Invokes get accessor
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">MyList<string> names = new();
names.Capacity = 100;   // Invokes set accessor
int i = names.Count;    // Invokes get accessor
int j = names.Capacity; // Invokes get accessor

Podobně jako pole a metody podporuje jazyk C# vlastnosti instance i statické vlastnosti. Statické vlastnosti jsou deklarovány pomocí statického modifikátoru a vlastnosti instance jsou deklarovány bez něj.

Přístupové objekty vlastnosti můžou být virtuální. Pokud deklarace vlastnosti obsahuje virtualmodifikátor , abstractnebo override , platí to pro přistupová zařízení vlastnosti.

Indexery

Indexer je člen, který umožňuje indexování objektů stejným způsobem jako pole. Indexer je deklarován jako vlastnost s tím rozdílem, že za názvem členu následuje this seznam parametrů zapsaný mezi oddělovače [ a ]. Parametry jsou k dispozici v přístupových objektech indexeru. Podobně jako vlastnosti můžou být indexery pro čtení i zápis, jen pro čtení a jen pro zápis a přístupové objekty indexeru můžou být virtuální.

Třída MyList<T> deklaruje jeden indexer pro čtení i zápis, který přebírá int parametr. Indexer umožňuje indexovat MyList<T> instance s int hodnotami. Například:


 names = new();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++)
{
    string s = names[i];
    names[i] = s.ToUpper();
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">MyList<string> names = new();
names.Add("Liz");
names.Add("Martha");
names.Add("Beth");
for (int i = 0; i < names.Count; i++)
{
    string s = names[i];
    names[i] = s.ToUpper();
}

Indexery mohou být přetíženy. Třída může deklarovat více indexerů, pokud se počet nebo typy jejich parametrů liší.

Události

Událost je člen, který umožňuje třídě nebo objektu poskytovat oznámení. Událost je deklarována jako pole s tím rozdílem, že deklarace obsahuje event klíčové slovo a typ musí být typ delegáta.

V rámci třídy, která deklaruje člena události, se událost chová stejně jako pole typu delegáta (za předpokladu, že událost není abstraktní a deklaruje přístupové objekty). Pole ukládá odkaz na delegáta, který představuje obslužné rutiny událostí, které byly přidány do události. Pokud nejsou k dispozici žádné obslužné rutiny událostí, pole je null.

Třída MyList<T> deklaruje jeden člen události s názvem Changed, který označuje, že do seznamu byla přidána nová položka nebo že položka seznamu byla změněna pomocí přístupového objektu sady indexerů. Událost Changed je vyvolána OnChanged virtuální metodou, která nejprve zkontroluje, jestli událost je null (to znamená, že nejsou k dispozici žádné obslužné rutiny). Pojem vyvolání události je přesně ekvivalentní vyvolání delegáta reprezentované událostí. Neexistují žádné speciální jazykové konstruktory pro vyvolání událostí.

Klienti reagují na události prostřednictvím obslužných rutin událostí. Obslužné rutiny událostí jsou připojeny pomocí operátoru += a odebrány pomocí operátoru -= . Následující příklad připojí obslužnou rutinu události k Changed události objektu MyList<string>.


();
        names.Changed += new EventHandler(ListChanged);
        names.Add("Liz");
        names.Add("Martha");
        names.Add("Beth");
        Console.WriteLine(s_changeCount); // "3"
    }
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">class EventExample
{
    static int s_changeCount;
    
    static void ListChanged(object sender, EventArgs e)
    {
        s_changeCount++;
    }
    
    public static void Usage()
    {
        var names = new MyList<string>();
        names.Changed += new EventHandler(ListChanged);
        names.Add("Liz");
        names.Add("Martha");
        names.Add("Beth");
        Console.WriteLine(s_changeCount); // "3"
    }
}

V pokročilých scénářích, kdy je potřeba řídit základní úložiště události, může deklarace události explicitně poskytnout add a remove přístupové objekty, které se podobají set přistupování vlastnosti.

Operátory

Operátor je člen, který definuje význam použití konkrétního operátoru výrazu na instance třídy. Lze definovat tři druhy operátorů: unární operátory, binární operátory a konverzní operátory. Všechny operátory musí být deklarovány jako public a static.

Třída MyList<T> deklaruje dva operátory operator == a operator !=. Tyto přepsané operátory dávají výrazům, které tyto operátory používají na MyList instance, nový význam. Konkrétně operátory definují rovnost dvou MyList<T> instancí při porovnávání každého z obsažených objektů pomocí svých Equals metod. Následující příklad používá == operátor k porovnání dvou MyList<int> instancí.


 a = new();
a.Add(1);
a.Add(2);
MyList b = new();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b);  // Outputs "True"
b.Add(3);
Console.WriteLine(a == b);  // Outputs "False"
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">MyList<int> a = new();
a.Add(1);
a.Add(2);
MyList<int> b = new();
b.Add(1);
b.Add(2);
Console.WriteLine(a == b);  // Outputs "True"
b.Add(3);
Console.WriteLine(a == b);  // Outputs "False"

První Console.WriteLine výstup, True protože dva seznamy obsahují stejný počet objektů se stejnými hodnotami ve stejném pořadí. Pokud MyList<T> by nebyl definován operator ==, první Console.WriteLine by měl výstup False , protože a a b odkazuje na různé MyList<int> instance.

Týden 10

Finalizační metody

Finalizátor je člen, který implementuje akce potřebné k dokončení instance třídy. K uvolnění nespravovaných prostředků se obvykle vyžaduje finalizace. Finalizátory nemohou mít parametry, nemůžou mít modifikátory přístupnosti a nelze je vyvolat explicitně. Finalizátor instance se vyvolá automaticky během uvolňování paměti. Další informace najdete v článku o finalizátorech.

Systém uvolňování paměti má povolenou širokou šířku při rozhodování, kdy shromažďovat objekty a spouštět finalizátory. Konkrétně časování volání finalizátoru není deterministické a finalizátory se můžou spouštět v libovolném vlákně. Z těchto a jiných důvodů by třídy měly implementovat finalizátory pouze v případě, že nejsou proveditelná žádná jiná řešení.

Příkaz using poskytuje lepší přístup ke zničení objektu.

Výrazy

Výrazy se konstruují z operandů a operátorů. Operátory výrazu označují, které operace se mají u operandů použít. Mezi příklady operátorů patří +-*/new. Mezi příklady operandů patří literály, pole, místní proměnné a výrazy.

Pokud výraz obsahuje více operátorů, jejich priorita určuje pořadí, ve kterém jsou jednotlivé operátory vyhodnocovány. Výraz se například vyhodnotí jakox + (y * z)x + y * z protože * operátor má vyšší prioritu + než operátor.

Pokud se operand vyskytuje mezi dvěma operátory se stejnou prioritou, asociativita operátorů řídí pořadí, ve kterém se operace provádějí:

  • S výjimkou operátorů přiřazení a spojování null jsou všechny binární operátory asociativní zleva, což znamená, že operace se provádějí zleva doprava. Například x + y + z se vyhodnotí jako (x + y) + z.
  • Operátory přiřazení, spojování a ??= operátory ?? null a podmíněný operátor ?: jsou asociativní zprava, což znamená, že operace se provádějí zprava doleva. Například x = y = z se vyhodnotí jako x = (y = z).

Prioritu a asociativitu lze řídit pomocí závorek. Například x + y * z nejprve vynásobí yz a pak přidá výsledek do x, ale (x + y) * z nejprve sečte x a y pak výsledek vynásobí .z

Většina operátorů může být přetížená. Přetížení operátoru umožňuje určit implementace operátoru definované uživatelem pro operace, kde jeden nebo oba operandy jsou typu definované uživatelem definované třídy nebo struktury.

C# poskytuje operátory pro provádění aritmetickýchlogickýchbitových a posunových operací a porovnávání rovnosti a pořadí .

Úplný seznam operátorů jazyka C# seřazených podle úrovně priority najdete v tématu Operátory jazyka C#.

Příkazy

Akce programu jsou vyjádřeny pomocí příkazů. Jazyk C# podporuje několik různých typů příkazů, z nichž řada je definována jako vložené příkazy.

  • Blok umožňuje zápis více příkazů v kontextech, ve kterých je povolen jeden příkaz. Blok se skládá ze seznamu příkazů zapsaných mezi oddělovači { a }.
  • Příkazy deklarace slouží k deklaraci místních proměnných a konstant.
  • K vyhodnocení výrazů se používají příkazy výrazů . Výrazy, které lze použít jako příkazy, zahrnují volání metod, přidělení objektů pomocí operátorunew, přiřazení pomocí = a složené operátory přiřazení, operace ++ zvýšení a snížení pomocí operátorů await a -- výrazů.
  • Příkazy výběru se používají k výběru jednoho z řady možných příkazů pro provedení na základě hodnoty některého výrazu. Tato skupina obsahuje příkazy if a switch .
  • Příkazy iterace se používají k opakovanému spuštění vloženého příkazu. Tato skupina obsahuje příkazy whilefordo, a foreach .
  • Příkazy jump se používají k přenosu řízení. Tato skupina obsahuje příkazy breakgotocontinuethrowreturn, a yield .
  • Příkaz try...catch se používá k zachycení výjimek, ke kterým dochází během provádění bloku, a trypříkaz ...finally se používá k určení kódu finalizace, který je vždy spuštěn, bez ohledu na to, zda došlo k výjimce nebo ne.
  • Příkazy checked a unchecked slouží k řízení kontextu kontroly přetečení pro aritmetické operace a převody integrálního typu.
  • Příkaz lock se používá k získání zámku vzájemného vyloučení pro daný objekt, spuštění příkazu a následnému uvolnění zámku.
  • Příkaz using se používá k získání prostředku, spuštění příkazu a následnému odstranění tohoto prostředku.

Následující seznam uvádí typy příkazů, které lze použít:

  • Deklarace místní proměnné.
  • Deklarace místní konstanty.
  • Výraz – příkaz.
  • if Prohlášení.
  • switch Prohlášení.
  • while Prohlášení.
  • do Prohlášení.
  • for Prohlášení.
  • foreach Prohlášení.
  • break Prohlášení.
  • continue Prohlášení.
  • goto Prohlášení.
  • return Prohlášení.
  • yield Prohlášení.
  • throw příkazy a try příkazy.
  • checked a unchecked příkazy.
  • lock Prohlášení.
  • using Prohlášení.

Týden 11

Pole, kolekce a LINQ

C# a .NET poskytují mnoho různých typů kolekcí. Pole mají syntaxi definovanou jazykem. Obecné typy kolekcí jsou uvedeny v System.Collections.Generic oboru názvů. Specializované kolekce zahrnují System.Span<T> přístup k nepřetržité paměti v rámci zásobníku a System.Memory<T> pro přístup k průběžné paměti ve spravované haldě. Všechny kolekce, včetně polí, Span<T>Memory<T> sdílejí jednotný princip iterace. Použijete System.Collections.Generic.IEnumerable<T> rozhraní. Tento princip sjednocení znamená, že kterýkoli z typů kolekcí lze použít s dotazy LINQ nebo jinými algoritmy. Metody, které používají IEnumerable<T> , a tyto algoritmy pracují s libovolnou kolekcí.

Pole

Pole je datová struktura, která obsahuje řadu proměnných, ke kterým se přistupuje prostřednictvím vypočítaných indexů. Proměnné obsažené v matici, označované také jako prvky pole, jsou všechny stejného typu. Tento typ se nazývá typ prvku pole.

Typy matice jsou odkazové typy a deklarace proměnné pole jednoduše vyhradí místo pro odkaz na instanci pole. Skutečné instance pole se vytvářejí dynamicky za běhu pomocí operátoru new . Operace new určuje délku nové instance pole, která se pak opraví po dobu životnosti instance. Indexy prvků pole jsou v rozsahu od 0 do Length - 1. Operátor new automaticky inicializuje prvky pole na výchozí hodnotu, což je například nula pro všechny číselné typy a null pro všechny odkazové typy.

Následující příklad vytvoří pole int prvků, inicializuje pole a vytiskne obsah pole.


int[] a = new int[10];
for (int i = 0; i < a.Length; i++)
{
    a[i] = i * i;
}
for (int i = 0; i < a.Length; i++)
{
    Console.WriteLine($"a[{i}] = {a[i]}");
}

Tento příklad vytvoří jednorozměrné pole a pracuje s tímto polem. Jazyk C# také podporuje vícerozměrná pole. Počet dimenzí typu pole, označovaný také jako pořadí typu matice, je jeden plus počet čárek mezi hranatými závorkami typu pole. Následující příklad přiděluje jednorozměrné, dvojrozměrné a trojrozměrné pole.


int[] a1 = new int[10];
int[,] a2 = new int[10, 5];
int[,,] a3 = new int[10, 5, 2];

Matice a1 obsahuje 10 prvků, a2 matice obsahuje 50 (10 × 5) prvků a a3 matice obsahuje 100 (10 × 5 × 2). Typ prvku pole může být libovolný typ, včetně typu pole. Matice s prvky typu pole se někdy nazývá jagged array , protože délky polí prvků nemusí být všechny stejné. Následující příklad přiděluje pole polí int:


int[][] a = new int[3][];
a[0] = new int[10];
a[1] = new int[5];
a[2] = new int[20];

První řádek vytvoří pole se třemi prvky, každý typ int[] a každý s počáteční hodnotou null. Další řádky pak inicializují tři prvky s odkazy na jednotlivé instance pole s různými délkami.

Operátor new umožňuje zadat počáteční hodnoty prvků pole pomocí inicializátoru pole, což je seznam výrazů zapsaných mezi oddělovači { a }. Následující příklad přidělí a inicializuje se int[] třemi prvky.


int[] a = new int[] { 1, 2, 3 };

Délka pole je odvozena z počtu výrazů mezi { a }. Inicializace pole je možné dále zkrátit tak, aby typ pole nemusel být restován.


int[] a = { 1, 2, 3 };

Oba předchozí příklady jsou ekvivalentní následujícímu kódu:


int[] t = new int[3];
t[0] = 1;
t[1] = 2;
t[2] = 3;
int[] a = t;

Příkaz foreach lze použít k vytvoření výčtu prvků libovolné kolekce. Následující kód vytvoří výčet pole z předchozího příkladu:


foreach (int item in a)
{
    Console.WriteLine(item);
}

Příkaz foreach používá IEnumerable<T> rozhraní, aby mohl pracovat s libovolnou kolekcí.

Interpolace řetězců

Interpolace řetězců jazyka C# umožňuje formátovat řetězce definováním výrazů, jejichž výsledky jsou umístěny ve formátovacím řetězci. Například následující příklad vytiskne teplotu v daný den ze sady dat o počasí:


Console.WriteLine($"The low and high temperature on {weatherData.Date:MM-dd-yyyy}");
Console.WriteLine($"    was {weatherData.LowTemp} and {weatherData.HighTemp}.");
// Output (similar to):
// The low and high temperature on 08-11-2020
//     was 5 and 30.

Interpolovaný řetězec je deklarován pomocí tokenu $ . Interpolace řetězců vyhodnotí výrazy mezi { výrazy a }pak převede výsledek na a stringnahradí text mezi hranatými závorkami řetězcovým výsledkem výrazu. V : prvním výrazu {weatherData.Date:MM-dd-yyyy} určuje formátovací řetězec. V předchozím příkladu určuje, že datum se má vytisknout ve formátu MM-dd-rrrr.

Porovnávání vzorů

Jazyk C# poskytuje vzorové odpovídající výrazy pro dotazování stavu objektu a spuštění kódu na základě tohoto stavu. Můžete zkontrolovat typy a hodnoty vlastností a polí a určit, která akce se má provést. Můžete také zkontrolovat prvky seznamu nebo pole. Výraz switch je primární výraz pro porovnávání vzorů.

Delegáti a výrazy lambda

Typ delegáta představuje odkazy na metody s konkrétním seznamem parametrů a návratovým typem. Delegáti umožňují považovat metody za entity, které lze přiřadit proměnným a předat je jako parametry. Delegáti se podobají konceptu ukazatelů funkcí nalezených v některých dalších jazycích. Na rozdíl od ukazatelů funkce jsou delegáti objektově orientované a typově bezpečné.

Následující příklad deklaruje a používá typ delegáta s názvem Function.


 _factor = factor;    public double Multiply(double x) => x * _factor;
}class DelegateExample
{
    static double[] Apply(double[] a, Function f)
    {
        var result = new double[a.Length];
        for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);
        return result;
    }    public static void Main()
    {
        double[] a = { 0.0, 0.5, 1.0 };
        double[] squares = Apply(a, (x) => x * x);
        double[] sines = Apply(a, Math.Sin);
        Multiplier m = new(2.0);
        double[] doubles = Apply(a, m.Multiply);
    }
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">delegate double Function(double x);class Multiplier
{
    double _factor;    public Multiplier(double factor) => _factor = factor;    public double Multiply(double x) => x * _factor;
}class DelegateExample
{
    static double[] Apply(double[] a, Function f)
    {
        var result = new double[a.Length];
        for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);
        return result;
    }    public static void Main()
    {
        double[] a = { 0.0, 0.5, 1.0 };
        double[] squares = Apply(a, (x) => x * x);
        double[] sines = Apply(a, Math.Sin);
        Multiplier m = new(2.0);
        double[] doubles = Apply(a, m.Multiply);
    }
}

Instance Function typu delegáta může odkazovat na libovolnou metodu double , která přebírá argument a vrací double hodnotu. Metoda Apply použije danou Function na prvky , double[]vrací double[] s výsledky. Main V metodě Apply se používá k použití tří různých funkcí na .double[]

Delegát může odkazovat na výraz lambda k vytvoření anonymní funkce (například (x) => x * x v předchozím příkladu), statické metody (například Math.Sin v předchozím příkladu) nebo metody instance (například m.Multiply v předchozím příkladu). Delegát, který odkazuje na metodu instance také odkazuje na konkrétní objekt, a když je metoda instance vyvolána prostřednictvím delegáta, tento objekt se stane this ve vyvolání.

Delegáty je možné vytvořit také pomocí anonymních funkcí nebo výrazů lambda, které jsou "vložené metody", které se vytvářejí při deklaraci. Anonymní funkce mohou zobrazit místní proměnné okolních metod. Následující příklad nevytvoří třídu:


 x * 2.0);
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">double[] doubles = Apply(a, (double x) => x * 2.0);

Delegát neví nebo se nezajímá o třídu metody, na které odkazuje. Odkazovaná metoda musí mít stejné parametry a návratový typ jako delegát.

async / await

Jazyk C# podporuje asynchronní programy se dvěma klíčovými slovy: async a await. Modifikátor přidáte async do deklarace metody, která deklaruje metodu, je asynchronní. Operátor await říká kompilátoru, aby asynchronně čekal na dokončení výsledku. Ovládací prvek se vrátí volajícímu a metoda vrátí strukturu, která spravuje stav asynchronní práce. Struktura je obvykle typu System.Threading.Tasks.Task<TResult>, ale může být libovolný typ, který podporuje model awaiter. Tyto funkce umožňují psát kód, který čte jako synchronní protějšek, ale provádí se asynchronně. Například následující kód stáhne domovskou stránku dokumentace Microsoftu:


 RetrieveDocsHomePage()
{
    var client = new HttpClient();
    byte[] content = await client.GetByteArrayAsync("https://learn.microsoft.com/");    Console.WriteLine($"{nameof(RetrieveDocsHomePage)}: Finished downloading.");
    return content.Length;
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public async Task<int> RetrieveDocsHomePage()
{
    var client = new HttpClient();
    byte[] content = await client.GetByteArrayAsync("https://learn.microsoft.com/");    Console.WriteLine($"{nameof(RetrieveDocsHomePage)}: Finished downloading.");
    return content.Length;
}

Tato malá ukázka ukazuje hlavní funkce pro asynchronní programování:

  • Deklarace metody obsahuje async modifikátor.
  • Tělo metody awaitje návrat metody GetByteArrayAsync .
  • Typ zadaný v return příkazu odpovídá argumentu typu v Task<T> deklaraci pro metodu. (Metoda, která vrátí Task příkazy use return bez argumentu).

Atributy

Typy, členy a další entity v programu jazyka C# podporují modifikátory, které řídí určité aspekty jejich chování. Například přístupnost metody se řídí pomocí publicmodifikátorů , protectedinternalprivate modifikátorů. Jazyk C# tuto schopnost zobecňuje tak, aby uživatelem definované typy deklarativních informací bylo možné připojit k entitám programu a načíst je za běhu. Programy určují tyto deklarativní informace definováním a použitím atributů.

Následující příklad deklaruje HelpAttribute atribut, který lze umístit do entit programu a poskytnout odkazy na jejich přidruženou dokumentaci.


 _url = url;    public string Url => _url;    public string Topic
    {
        get => _topic;
        set => _topic = value;
    }
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public class HelpAttribute : Attribute
{
    string _url;
    string _topic;    public HelpAttribute(string url) => _url = url;    public string Url => _url;    public string Topic
    {
        get => _topic;
        set => _topic = value;
    }
}

Všechny třídy atributů jsou odvozeny od Attribute základní třídy poskytované knihovnou .NET. Atributy lze použít zadáním jejich názvu spolu s libovolnými argumenty uvnitř hranatých závorek těsně před přidruženou deklarací. Pokud název atributu končí Attribute, může být tato část názvu vynechána při odkazování na atribut. Můžete ho HelpAttribute například použít následujícím způsobem.


[Help("https://learn.microsoft.com/dotnet/csharp/tour-of-csharp/features")]
public class Widget
{
    [Help("https://learn.microsoft.com/dotnet/csharp/tour-of-csharp/features",
    Topic = "Display")]
    public void Display(string text) { }
}

Tento příklad připojí třídu HelpAttributeWidget . Přidá další HelpAttribute metodu Display ve třídě. Veřejné konstruktory třídy atributu řídí informace, které musí být poskytnuty, když je atribut připojen k entitě programu. Další informace lze poskytnout odkazováním na veřejné vlastnosti pro čtení a zápis třídy atributu (například odkaz na Topic vlastnost dříve).

Metadata definovaná atributy je možné číst a zpracovávat za běhu pomocí reflexe. Pokud je požadován konkrétní atribut pomocí této techniky, konstruktor třídy atributu je vyvolán s informacemi zadanými ve zdroji programu. Vrátí se výsledná instance atributu. Pokud byly prostřednictvím vlastností poskytnuty další informace, jsou tyto vlastnosti nastaveny na dané hodnoty před vrácením instance atributu.

Následující ukázka kódu ukazuje, jak získat HelpAttribute instance přidružené ke Widget třídě a jeho Display metodě.


 0)
{
    HelpAttribute attr = (HelpAttribute)widgetClassAttributes[0];
    Console.WriteLine($"Widget class help URL : {attr.Url} - Related topic : {attr.Topic}");
}System.Reflection.MethodInfo displayMethod = widgetType.GetMethod(nameof(Widget.Display));object[] displayMethodAttributes = displayMethod.GetCustomAttributes(typeof(HelpAttribute), false);if (displayMethodAttributes.Length > 0)
{
    HelpAttribute attr = (HelpAttribute)displayMethodAttributes[0];
    Console.WriteLine($"Display method help URL : {attr.Url} - Related topic : {attr.Topic}");
}
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">Type widgetType = typeof(Widget);object[] widgetClassAttributes = widgetType.GetCustomAttributes(typeof(HelpAttribute), false);if (widgetClassAttributes.Length > 0)
{
    HelpAttribute attr = (HelpAttribute)widgetClassAttributes[0];
    Console.WriteLine($"Widget class help URL : {attr.Url} - Related topic : {attr.Topic}");
}System.Reflection.MethodInfo displayMethod = widgetType.GetMethod(nameof(Widget.Display));object[] displayMethodAttributes = displayMethod.GetCustomAttributes(typeof(HelpAttribute), false);if (displayMethodAttributes.Length > 0)
{
    HelpAttribute attr = (HelpAttribute)displayMethodAttributes[0];
    Console.WriteLine($"Display method help URL : {attr.Url} - Related topic : {attr.Topic}");
}

Týden 12

Opakování znalostí a zápočtový test.

Týden 13

Odevzdávání zápočtových projektů.