Pouze tento týden sleva až 80 % na e-learning týkající se C# .NET. Zároveň využij akce až 80 % zdarma při nákupu e-learningu - více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.
C# .NET week + discount week

Lekce 9 - Pole v C# .NET

V minulé lekci kurzu, Ošetření uživatelských vstupů v C# .NET, jsme si ukázali ošetření uživatelských vstupů.

Dnes si v C# tutoriálu představíme datovou strukturu pole a vyzkoušíme si, co všechno umí.

Pole

Představte si, že si chcete uložit nějaké údaje o více prvcích. Např. chcete v paměti uchovávat 10 čísel, políčka šachovnice nebo jména 50ti uživatelů. Asi vám dojde, že v programování bude nějaká lepší cesta, než začít bušit proměnné uzivatel1, uzivatel2, ... až uzivatel50. Nehledě na to, že jich může být třeba 1000. A jak by se v tom potom hledalo? Brrr, takle ne :)

Pokud potřebujeme uchovávat větší množství proměnných stejného typu, tento problém nám řeší pole. Můžeme si ho představit jako řadu přihrádek, kde v každé máme uložený jeden prvek. Přihrádky jsou očíslované tzv. indexy, první má index 0.

Struktura pole

(Na obrázku je vidět pole osmi čísel)

Programovací jazyky se velmi liší v tom, jak s polem pracují. V některých jazycích (zejména starších, kompilovaných) nebylo možné za běhu programu vytvořit pole s dynamickou velikostí (např. mu dát velikost dle nějaké proměnné). Pole se muselo deklarovat s konstantní velikostí přímo ve zdrojovém kódu. Toto se obcházelo tzv. pointery a vlastními datovými strukturami, což často vedlo k chybám při manuální správě paměti a nestabilitě programu (např. v C++). Naopak některé interpretované jazyky umožňují nejen deklarovat pole s libovolnou velikostí, ale dokonce tuto velikost na již existujícím poli měnit (např. PHP). My víme, že C# je virtuální stroj, tedy cosi mezi kompilerem a interpretem. Proto můžeme pole založit s velikostí, kterou dynamicky zadáme až za běhu programu, ale velikost existujícího pole modifikovat nemůžeme. Lze to samozřejmě obejít nebo použít jiné datové struktury, ale k tomu se dostaneme.

Možná vás napadá, proč se tu zabýváme s polem, když má evidentně mnoho omezení a existují lepší datové struktury. Odpověď je prostá: pole je totiž jednoduché. Nemyslím pro nás na pochopení (to také), ale zejména pro C#. Rychle se s ním pracuje, protože prvky jsou v paměti jednoduše uloženy za sebou, zabírají všechny stejně místa a rychle se k nim přistupuje. Mnoho vnitřních funkčností v .NET proto nějak pracuje s polem nebo pole vrací. Je to klíčová struktura.

Pro hromadnou manipulaci s prvky pole se používají cykly.

Pole deklarujeme pomocí hranatých závorek:

int[] pole;

Na české klávesnici jsou hranaté závorky ukryté pod Pravým Alt a klávesami F a G:

Hranaté závorky

Výraz pole je samozřejmě název naší proměnné. Nyní jsme však pouze deklarovali, že v proměnné bude pole intů. Nyní ho musíme založit, abychom ho mohli používat. Použijeme k tomu klíčové slovo new, které na naší úrovni znalostí nejsme zatím schopní podrobně vysvětlit. Spokojme se s tím, že je to kvůli tomu, že je pole referenční datový typ (můžeme chápat jako složitější typ):

int[] pole = new int[10];

Nyní máme v proměnné pole pole o velikosti deseti intů.

K prvkům pole potom přistupujeme přes hranatou závorku, pojďme na první index (tedy index 0) uložit číslo 1.

int[] pole = new int[10];
pole[0] = 1;

Plnit pole takhle ručně by bylo příliš pracné, použijeme cyklus a naplníme si pole čísly od 1 do 10. K naplnění použijeme for cyklus:

int[] pole = new int[10];
pole[0] = 1;
for (int i = 0; i < 10; i++)
    pole[i] = i + 1;

Abychom pole vypsali, můžeme za předchozí kód připsat:

for (int i = 0; i < pole.Length; i++)
    Console.Write("{0} ", pole[i]);
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Všimněte si, že pole má vlastnost Length, kde je uložena jeho délka, tedy počet prvků.

Konzolová aplikace
1 2 3 4 5 6 7 8 9 10

Můžeme použít zjednodušenou verzi cyklu pro práci s kolekcemi, známou jako foreach. Ten projede všechny prvky v poli a jeho délku si zjistí sám. Jeho syntaxe je následující:

foreach (datovytyp promenna in kolekce)
{
    // příkazy
}

Cyklus projede prvky v kolekci (to je obecný název pro struktury, které obsahují více prvků, u nás to bude pole) postupně od prvního do posledního. Prvek máme v každé iteraci cyklu uložený v dané proměnné.

Také pro tento cyklus existuje ve Visual Studio užitečný snippet: napíšeme foreach a dvakrát stiskneme Tabulátor, zbytek cyklu se sám dopíše. Zkuste si to.

Přepišme tedy náš dosavadní program pro foreach. Cyklus foreach nemá řídící proměnnou, není tedy vhodný pro vytvoření našeho pole a použijeme ho jen pro výpis.

int[] pole = new int[10];
pole[0] = 1;
for (int i = 0; i < 10; i++)
    pole[i] = i + 1;
foreach (int i in pole)
    Console.Write("{0} ", i);
Console.ReadKey();

Výstup programu:

Konzolová aplikace
1 2 3 4 5 6 7 8 9 10

Pole samozřejmě můžeme naplnit ručně a to i bez toho, abychom dosazovali postupně do každého indexu. Použijeme k tomu složených závorek a prvky oddělujeme čárkou:

string[] simpsonovi = {"Homer", "Marge", "Bart", "Lisa", "Maggie"};

Pole často slouží k ukládání mezivýsledků, které se potom dále v programu používají. Když potřebujeme nějaký výsledek 10x, tak to nebudeme 10x počítat, ale spočítáme to jednou a uložíme do pole, odtud poté výsledek jen načteme.

Metody na třídě Array

.NET nám poskytuje třídu Array, která obsahuje pomocné metody pro práci s poli. Pojďme se na ně podívat:

Sort()

Jak již název napovídá, metoda nám pole seřadí. Její jediný parametr je pole, které chceme seřadit. Je dokonce tak chytrá, že pracuje podle toho, co máme v poli uložené. Stringy třídí podle abecedy, čísla podle velikosti. Zkusme si setřídit a vypsat naši rodinku Simpsnů:

string[] simpsonovi = {"Homer", "Marge", "Bart", "Lisa", "Maggie"};
Array.Sort(simpsonovi);
foreach (string s in simpsonovi)
    Console.Write("{0} ", s);
Console.ReadKey();

Konzolová aplikace
Bart Homer Lisa Maggie Marge

Zkuste si udělat pole čísel a vyzkoušejte si, že to opravdu funguje i pro ně.

Reverse()

Reverse() nám pole otočí (první prvek bude jako poslední atd.), toho můžeme využít např. pro třídění pozpátku:

string[] simpsonovi = {"Homer", "Marge", "Bart", "Lisa", "Maggie"};
Array.Sort(simpsonovi);
Array.Reverse(simpsonovi);
foreach (string s in simpsonovi)
    Console.Write("{0} ", s);
Console.ReadKey();

IndexOf() a LastIndexOf()

Tyto metody vrátí index prvního nebo posledního nalezeného prvku. V případě nenalezení prvku vrátí -1. Každá z metod bere dva parametry, prvním je pole, druhým hledaný prvek. Umožníme uživateli zadat jméno Simpsna a řekneme mu, na jaké pozici je uložený. Teď to pro nás nemá hlubší význam, protože prvek pole je jen string. Bude se nám to však velmi hodit ve chvíli, kdy v poli budeme mít uloženy plnohodnotné objekty. Berme to tedy jako takovou přípravu.

string[] simpsonovi = {"Homer", "Marge", "Bart", "Lisa", "Maggie"};
Console.WriteLine("Ahoj, zadej svého oblíbeného Simpsna (z rodiny Simpsnů): ");
string simpson = Console.ReadLine();
int pozice = Array.IndexOf(simpsonovi, simpson);
if (pozice >= 0)
    Console.WriteLine("Jo, to je můj {0}. nejoblíbenější Simpson!", pozice + 1);
else
    Console.WriteLine("Hele, tohle není Simpson!");
Console.ReadKey();

Konzolová aplikace
Ahoj, zadej svého oblíbeného Simpsna (z rodiny Simpsnů):
Homer
Jo, to je můj 1. nejoblíbenější Simpson!

Copy()

Copy() již podle názvu zkopíruje část pole do jiného pole. Prvním parametrem je zdrojové pole, druhým cílové a třetím počet prvků, který se má zkopírovat.

Metody na poli

Třída Array není jedinou možností, jak s polem manipulovat. Přímo na samotné instanci pole (konkrétní proměnné) můžeme volat také spoustu metod. I když si zmíníme jen některé, je jich opravdu hodně. Nebudeme tedy dělat příklady, jen si je popíšeme:

Length

Length jsme si již zmínili, vrátí délku pole. Není metodou, ale vlastností, nepíší se za ni tedy závorky ().

Následující metody jsou takzvané "extension metody". Podrobněji je probereme později. V tuto chvíli musíte jen vědět, že pro jejich zavolání potřebujeme balíček: using System.Linq; (tento kód si dopište pod: using System;)

Min(), Max(), Average(), Sum()

Matematické metody, vracející nejmenší prvek (Min()), největší prvek (Max()), průměr ze všech prvků (Average()) a součet všech prvků (Sum()). Metody nemají žádné parametry.

Concat(), Intersect(), Union()

Všechny tyto metody vrátí na výstupu nové pole a jako parametr mají druhé pole. Concat() vykoná nám již známou konkatenaci, tedy k našemu poli připojí druhé pole a takto vzniklé nové pole vrátí. Intersect() vykoná průnik obou polí, tedy sestaví pole s prvky, které jsou oběma polím společné. Union() naopak vykoná sjednocení, funguje tedy podobně jako Concat(), jen prvky, které byly v obou polích, jsou v novém poli jen jednou.

First() a Last()

Již podle názvu metody vrátí první a poslední prvek, neberou žádné parametry.

Take() a Skip()

Obě tyto metody berou jako parametr počet prvků. Take() vrátí pole s daným počtem prvků zkopírovaných od začátku původního pole. Skip() naopak vrátí pole bez těchto prvních prvků.

Contains()

Metoda vrací true/false podle toho, zda se prvek, uvedený v parametru metody, v daném poli nachází.

Reverse()

Metodu Reverse() známe již z třídy Array, pokud ji ale voláme na konkrétním poli, tak se prvky v něm neotočí, nýbrž je vytvořeno nové otočené pole a to je vráceno. Metoda nemá žádné parametry.

Distinct()

Distinct() je metoda bez parametrů a zajistí, aby byl v poli každý prvek jen jednou, tedy vymaže duplicitní prvky a unikátní pole vrátí jako návratovou hodnotu metody, opět tedy nemodifikuje dané pole.

Mnoho metod nemění přímo naše pole, ale vrátí pouze pole nové (jsou to metody Concat(), Intersect(), Union(), Reverse() a Distinct()), ve kterém jsou provedeny požadované změny. Pokud chceme modifikovat původní pole, musíme do něj dosadit. Tyto metody bohužel z důvodů, které pochopíme až později, nevrací přímo pole, ale typ IEnumerable. Aby bylo dosazení výsledku zpět do pole možné, musíme ho ještě převést na pole metodou ToArray().

int[] cisla = { 1, 2, 3, 3, 3, 5 };
cisla = cisla.Distinct().ToArray();

Proměnná délka pole

Říkali jsme si, že délku pole můžeme definovat i za běhu programu, pojďme si to zkusit a rovnou si vyzkoušejme nějakou metodu na poli:

Console.WriteLine("Ahoj, spočítám ti průměr známek. Kolik známek zadáš?");
int pocet = int.Parse(Console.ReadLine());
int[] cisla = new int[pocet];
for (int i = 0; i < pocet; i++)
{
    Console.Write("Zadejte {0}. číslo: ", i + 1);
    cisla[i] = int.Parse(Console.ReadLine());
}
Console.WriteLine("Průměr tvých známek je: {0}", cisla.Average());
Console.ReadKey();

Konzolová aplikace
Ahoj, spočítám ti průměr známek. Kolik známek zadáš?
5
Zadejte 1. číslo: 1
Zadejte 2. číslo: 2
Zadejte 3. číslo: 2
Zadejte 4. číslo: 3
Zadejte 5. číslo: 5
Průměr tvých známek je: 2,6

Tento příklad by šel samozřejmě napsat i bez použití pole, ale co kdybychom chtěli spočítat např. medián? Nebo např. vypsat zadaná čísla pozpátku? To už by bez pole nešlo. Takhle máme k dispozici v poli původní hodnoty a můžeme s nimi neomezeně a jednoduše pracovat.

To by pro dnešek stačilo, můžete si s polem hrát.

V následujícím cvičení, Řešené úlohy k 8.-9. lekci C# .NET, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 1272x (141.34 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Ošetření uživatelských vstupů v C# .NET
Všechny články v sekci
Základní konstrukce jazyka C# .NET
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 8.-9. lekci C# .NET
Článek pro vás napsal David Čápka
Avatar
Uživatelské hodnocení:
214 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 13 let. Má rád Nirvanu, sushi a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity

 

 

Komentáře
Zobrazit starší komentáře (175)

Avatar
Libor Szücs
Člen
Avatar
Libor Szücs:4.11.2021 11:58

Ahoj,
Při používání například metody Concat() zavolané na pole musím použít ještě.ToArray(), aby to fungovalo. Proč to u těchto metod není v článku napsané? Taky je možný, že se pletu a použít se to nemusí nebo jsem někde něco přehlídl. Vysvětlí mi to někdo prosím?
Děkuji.

 
Odpovědět
4.11.2021 11:58
Avatar
Libor Szücs
Člen
Avatar
Odpovídá na Libor Szücs
Libor Szücs:4.11.2021 12:05

Dotaz beru zpět, přehlídl jsem vysvětlení v článku.

 
Odpovědět
4.11.2021 12:05
Avatar
Neaktivní uživatel:4.11.2021 15:02

Ahoj!
mám problém s následujícím:

string[] simpsonovi = { "Homer", "Marge", "Bart", "Lisa", "Maggie" };
            Console.WriteLine(simpsonovi.Contains("Bart"));

Zde na stránkách mi tento kousek programu funguje, ale u mě na:

Microsoft Visual Studio Community 2019
Verze 16.11.5 (čeština)

mi hlásí chybu:

Chyba CS1929 string[] neobsahuje definici pro Contains a přetížení optimální metody rozšíření MemoryExtensi­ons.Contains<strin­g>(ReadOnlySpan<strin­g>, string) vyžaduje přijímač typu ReadOnlySpan<strin­g>.

Podobný problém jsem měl i u řešení Ovoce-zelenina v následující lekci. Zkopíroval jsem řešení itnetwork a také chyba. Pokud ale místo pole použiji jen string, nebo prvek pole, tak to funguje.

V mém řešení jsem využil:

if (Array.IndexOf(zeleniny, produkt) > -1)
                    Console.WriteLine("Zadal jsi zeleninu");

Děkuji za pomoc!

Odpovědět
4.11.2021 15:02
Neaktivní uživatelský účet
Avatar
Karel Karafiát:22.11.2021 11:34

Ahoj, mám dotaz ohledně seřazení dle abecedy. Jak je to s písmenem CH? To v US abecedě není - takže to dle naší abecedy není 100%.

 
Odpovědět
22.11.2021 11:34
Avatar
Ladislav SKOKAN:30.12.2021 17:21

Pro opravdové řazení kombinace text+číslo nebo číslo+text je vždy vhodnější použít vlastní rutinu, která rozezná co je text a co číslo. Vyhneme se tak nesmyslům řazení jako je např. 1, 100, 15 atd...

 
Odpovědět
30.12.2021 17:21
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!
Avatar
Odpovídá na Karel Karafiát
vratislav.hirs:9. ledna 20:41

Asi se dá definovat, podle které abecedy se má třídit.
Viz následující kód.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Globalization;
using System.Threading;

namespace ArraySort
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] seznam = { "I","H","CH","C"};
            Console.WriteLine("Opis výchozího seznamu");
            foreach (string prvek in seznam)
            Console.Write(prvek + ", ");
            Console.WriteLine();

            Array.Sort(seznam);
            Console.WriteLine("Třídění dle výchozí CultureInfo (cs-CZ)");
            foreach (string prvek in seznam)
            Console.Write(prvek+", ");
            Console.WriteLine();

            //změna třídění na en-US
            CultureInfo ci = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentCulture = ci;

            Array.Sort(seznam);
            Console.WriteLine("Třídění dle CultureInfo en-US");
            foreach (string prvek in seznam)
            Console.Write(prvek + ", ");
            Console.WriteLine();

            //změna třídění na sk-SK
            ci = new CultureInfo("sk-SK");
            Thread.CurrentThread.CurrentCulture = ci;

            Array.Sort(seznam);
            Console.WriteLine("Třídění dle CultureInfo sk-SK");
            foreach (string prvek in seznam)
            Console.Write(prvek + ", ");

            Console.ReadKey();
        }
    }
}
 
Odpovědět
9. ledna 20:41
Avatar
Tomáš Koutecký:27. února 10:42

Ocenil bych příklady k metodám: Concat(), Intersect() a Union(), protože z popisu to není uplně jasné, co se stane.

 
Odpovědět
27. února 10:42
Avatar
Tomáš Havránek :15. května 16:58

Stále se plácám v pochopení pole. Pokud vezmu tento kód, tedy zadávání pole za běhu programu:

Console.WriteLine("Ahoj, spočítám ti průměr známek. Kolik známek zadáš?");
int pocet = int.Parse(Console.ReadLine());
int[] cisla = new int[pocet];
for (int i = 0; i < pocet; i++)
{
    Console.Write("Zadejte {0}. číslo: ", i + 1);
    cisla[i] = int.Parse(Console.ReadLine());
}
Console.WriteLine("Průměr tvých známek je: {0}", cisla.Average());
Console.ReadKey();

respektive tuto část

cisla[i] = int.Parse(Console.ReadLine());

Co to dělá to chápu. Ale potřebuji si nějak zdůvodnit nebo spíš pojmenovat "cisla[i]", mě to nutí tam psát:

i = int.Parse(Console.ReadLine());

To samozřejmě nefunguje, ale jak si mám nazvat cisla[i]. Hodnoty "i" v poli čísla? Mě ten zápis nejde do hlavy a jakmile si to nezdůvodním, tak nemůžu dál. Někdo ochotný mi to pojmenovat?

 
Odpovědět
15. května 16:58
Avatar
Odpovídá na Tomáš Havránek
Lukáš Bartošek :16. května 12:03
Console.WriteLine("Ahoj, spočítám ti průměr známek. Kolik známek zadáš?");
int pocet = int.Parse(Console.ReadLine());               //Zadáš velikost pole
int[] cisla = new int[pocet];            //Vytvoří se Pole o velikosti co jsi zadal
for (int i = 0; i < pocet; i++)          /*For cyklus proiteruje počet polí co jsi zadal. Program musí
                                          vědět do jakého pole zadávaš čislo.
                                          Tj. cyklus 0 >> i=0  cisla[0] = zadane čislo
                                          Tj. cyklus 3 >> i=3  cisla[3] = zadane čislo */

{
    Console.Write("Zadejte {0}. číslo: ", i + 1);
    cisla[i] = int.Parse(Console.ReadLine());
}
Console.WriteLine("Průměr tvých známek je: {0}", cisla.Average());
Console.ReadKey();

a pokud by jsi zadal

i = int.Parse(Console.ReadLine());

Tak by ti to do proměné s názvem i zapsalo zadanou hodnotu.
Fungovat ti to nebude jelikož jsi nevytvořil proměnou s daným názvem.

Snad jsem pomohl kdyby ne tak zkus znovu zadat otázku :D

P.S. nejsi z Olomouce?

Odpovědět
16. května 12:03
Nachystejte uzenáče, na snídani jsem zpátky!
Avatar
Odpovídá na Lukáš Bartošek
Tomáš Havránek :18. května 9:47

Ahoj, děkuju za rychlou odpověď. Přesně takhle to prostě občas potřebuju popsat, abych to pochopil 🙂 Spousta věcí je ve finále logická a jednoduchá, ale pokud jí nemám popsanou doslovně je těžší to uchopit. Olomouc ne, jsem z Benešova.

 
Odpovědět
18. května 9:47
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 185. Zobrazit vše