Školení
Modul
Ukládání a procházení posloupností dat pomocí polí a příkazu foreach v jazyce C# - Training
Naučte se vytvářet proměnné pole a iterovat prvky pole.
Tento prohlížeč se už nepodporuje.
Upgradujte na Microsoft Edge, abyste mohli využívat nejnovější funkce, aktualizace zabezpečení a technickou podporu.
Téměř každý program, který napíšete, bude potřebovat iterovat přes kolekci. Napíšete kód, který prozkoumá všechny položky v kolekci.
Vytvoříte také metody iterátoru, což jsou metody, které vytvářejí iterátor pro prvky této třídy. Iterátor je objekt, který prochází kontejnerem, zejména seznamy. Iterátory lze použít pro:
Jazyk C# poskytuje funkce pro generování i využívání sekvencí. Tyto sekvence lze vytvořit a využívat synchronně nebo asynchronně. Tento článek obsahuje přehled těchto funkcí.
Vytvoření výčtu kolekce je jednoduché: Klíčové foreach
slovo vyčíslí kolekci a spustí vložený příkaz jednou pro každý prvek v kolekci:
foreach (var item in collection)
{
Console.WriteLine(item?.ToString());
}
To je všechno. K iteraci veškerého obsahu kolekce stačí příkaz foreach
. Ten foreach
výrok ale není magický. Spoléhá na dvě obecná rozhraní definovaná v knihovně .NET Core k vygenerování kódu potřebného k iteraci kolekce: IEnumerable<T>
a IEnumerator<T>
. Tento mechanismus je podrobněji vysvětlen níže.
Obě tato rozhraní mají také ne generické protějšky: IEnumerable
a IEnumerator
. Obecné verze jsou upřednostňované pro moderní kód.
Pokud se sekvence generuje asynchronně, můžete pomocí await foreach
příkazu asynchronně využívat sekvenci:
await foreach (var item in asyncSequence)
{
Console.WriteLine(item?.ToString());
}
Pokud je System.Collections.Generic.IEnumerable<T>sekvence , použijete foreach
. Pokud je System.Collections.Generic.IAsyncEnumerable<T>sekvence , použijete await foreach
. V druhém případě se sekvence generuje asynchronně.
Další skvělá funkce jazyka C# umožňuje vytvářet metody, které vytvářejí zdroj pro výčet. Tyto metody se označují jako metody iterátoru. Metoda iterátoru definuje, jak vygenerovat objekty v sekvenci při vyžádání. Kontextová klíčová slova slouží yield return
k definování metody iterátoru.
Tuto metodu můžete napsat, abyste vytvořili sekvenci celých čísel od 0 do 9:
public IEnumerable<int> GetSingleDigitNumbers()
{
yield return 0;
yield return 1;
yield return 2;
yield return 3;
yield return 4;
yield return 5;
yield return 6;
yield return 7;
yield return 8;
yield return 9;
}
Výše uvedený kód zobrazuje odlišné yield return
příkazy, které zvýrazňují skutečnost, že v metodě iterátoru můžete použít více diskrétních yield return
příkazů. K zjednodušení kódu metody iterátoru můžete (a často také použít) konstruktory jiného jazyka. Následující definice metody vytvoří přesně stejnou sekvenci čísel:
public IEnumerable<int> GetSingleDigitNumbersLoop()
{
int index = 0;
while (index < 10)
yield return index++;
}
Nemusíte se rozhodnout o jednom nebo druhém. Pro splnění potřeb vaší metody můžete mít libovolný počet yield return
příkazů:
public IEnumerable<int> GetSetsOfNumbers()
{
int index = 0;
while (index < 10)
yield return index++;
yield return 50;
index = 100;
while (index < 110)
yield return index++;
}
Všechny tyto předchozí příklady by měly asynchronní protějšek. V každém případě byste nahradili návratový IEnumerable<T>
typ znakem IAsyncEnumerable<T>
. Předchozí příklad by měl například následující asynchronní verzi:
public async IAsyncEnumerable<int> GetSetsOfNumbersAsync()
{
int index = 0;
while (index < 10)
yield return index++;
await Task.Delay(500);
yield return 50;
await Task.Delay(500);
index = 100;
while (index < 110)
yield return index++;
}
To je syntaxe synchronních i asynchronních iterátorů. Pojďme se podívat na skutečný příklad. Představte si, že používáte projekt IoT a senzory zařízení generují velmi velký datový proud. Pokud chcete získat pocit z dat, můžete napsat metodu, která vzorkuje každý Nth datový prvek. Tato malá metoda iterátoru provede trik:
public static IEnumerable<T> Sample<T>(this IEnumerable<T> sourceSequence, int interval)
{
int index = 0;
foreach (T item in sourceSequence)
{
if (index++ % interval == 0)
yield return item;
}
}
Pokud čtení ze zařízení IoT vytvoří asynchronní sekvenci, upravíte metodu, jak ukazuje následující metoda:
public static async IAsyncEnumerable<T> Sample<T>(this IAsyncEnumerable<T> sourceSequence, int interval)
{
int index = 0;
await foreach (T item in sourceSequence)
{
if (index++ % interval == 0)
yield return item;
}
}
Existuje jedno důležité omezení metod iterátoru: nemůžete mít příkaz return
i yield return
příkaz ve stejné metodě. Následující kód se nezkompiluje:
public IEnumerable<int> GetSingleDigitNumbers()
{
int index = 0;
while (index < 10)
yield return index++;
yield return 50;
// generates a compile time error:
var items = new int[] {100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };
return items;
}
Toto omezení obvykle není problém. Máte možnost buď použít yield return
v rámci této metody, nebo oddělit původní metodu do více metod, některé pomocí return
a některé pomocí yield return
.
Poslední metodu můžete mírně upravit tak, aby se používala yield return
všude:
public IEnumerable<int> GetFirstDecile()
{
int index = 0;
while (index < 10)
yield return index++;
yield return 50;
var items = new int[] {100, 101, 102, 103, 104, 105, 106, 107, 108, 109 };
foreach (var item in items)
yield return item;
}
V některých případech je správnou odpovědí rozdělení metody iterátoru na dvě různé metody. Jeden, který používá return
, a sekundu, která používá yield return
. Představte si situaci, kdy můžete chtít vrátit prázdnou kolekci nebo prvních pět lichých čísel na základě logického argumentu. Můžete napsat tyto dvě metody:
public IEnumerable<int> GetSingleDigitOddNumbers(bool getCollection)
{
if (getCollection == false)
return new int[0];
else
return IteratorMethod();
}
private IEnumerable<int> IteratorMethod()
{
int index = 0;
while (index < 10)
{
if (index % 2 == 1)
yield return index;
index++;
}
}
Podívejte se na výše uvedené metody. První používá standardní return
příkaz k vrácení prázdné kolekce nebo iterátoru vytvořeného druhou metodou. Druhá metoda pomocí yield return
příkazu vytvoří požadovanou sekvenci.
Tento foreach
příkaz se rozšiřuje na standardní idiom, který používá IEnumerable<T>
a IEnumerator<T>
rozhraní k iteraci napříč všemi prvky kolekce. Zároveň minimalizuje chyby, které vývojáři dělají tím, že nespravují správně prostředky.
Kompilátor přeloží smyčku foreach
zobrazenou v prvním příkladu do něčeho podobného jako tento konstruktor:
IEnumerator<int> enumerator = collection.GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
Console.WriteLine(item.ToString());
}
Přesný kód vygenerovaný kompilátorem je složitější a zpracovává situace, kdy objekt vrácený GetEnumerator()
implementací IDisposable
rozhraní. Úplné rozšíření vygeneruje kód podobně jako tento:
{
var enumerator = collection.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
var item = enumerator.Current;
Console.WriteLine(item.ToString());
}
}
finally
{
// dispose of enumerator.
}
}
Kompilátor přeloží první asynchronní ukázku do něčeho podobného tomuto konstruktoru:
{
var enumerator = collection.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
var item = enumerator.Current;
Console.WriteLine(item.ToString());
}
}
finally
{
// dispose of async enumerator.
}
}
Způsob, jakým je enumerátor uvolněn, závisí na vlastnostech typu .enumerator
V obecném synchronním případě finally
se klauzule rozšíří na:
finally
{
(enumerator as IDisposable)?.Dispose();
}
Obecný asynchronní případ se rozbalí na:
finally
{
if (enumerator is IAsyncDisposable asyncDisposable)
await asyncDisposable.DisposeAsync();
}
Pokud je však typ enumerator
zapečetěného typu a neexistuje žádný implicitní převod z typu enumerator
na IDisposable
nebo IAsyncDisposable
, finally
klauzule se rozšíří na prázdný blok:
finally
{
}
Pokud existuje implicitní převod z typu enumerator
na IDisposable
a enumerator
je nenulový typ hodnoty, finally
klauzule se rozšíří na:
finally
{
((IDisposable)enumerator).Dispose();
}
Naštěstí si nemusíte pamatovat všechny tyto podrobnosti. Tento foreach
příkaz se stará o všechny ty drobné odlišnosti za vás. Kompilátor vygeneruje správný kód pro některý z těchto konstruktorů.
Zpětná vazba k produktu .NET
.NET je open source projekt. Vyberte odkaz pro poskytnutí zpětné vazby:
Školení
Modul
Ukládání a procházení posloupností dat pomocí polí a příkazu foreach v jazyce C# - Training
Naučte se vytvářet proměnné pole a iterovat prvky pole.