Iterationsinstruktioner

Tip

Den här artikeln är en del av avsnittet Grunderna för utvecklare som redan känner till minst ett programmeringsspråk och lär sig C#. Om du inte har använt programmering tidigare börjar du med självstudierna Komma igång först. Fullständig syntax finns i iterationsinstruktioner i språkreferensen.

Kommer du från ett annat språk? Alla fyra C#-loopar (foreach, while,do-while och for) har direkta motsvarigheter i Java, C++ och JavaScript. Du använder foreach oftast. Den itererar över en samling utan ett index, som Javas utökade for eller Javascripts for...of.

Iterationsinstruktioner kör ett kodblock upprepade gånger. Varje passage genom blocket är en iteration och ett upprepande block är en loop. C# innehåller fyra loopar. Börja med foreach för samlingar, använd while och do-while när ett villkor styr upprepningen och använd for när du behöver ett explicit index.

Iterera en samling med foreach

Instruktionen foreach kör sin brödtext en gång för varje element i en samling i ordning. Det är det vanligaste valet för att läsa en samling eftersom du inte hanterar ett index eller en gränskontroll. Satsen foreach förhindrar typiska ettfel:

string[] names = ["Ana", "Ben", "Cleo"];

// foreach reads each element in order. It's the default loop for
// collections: no index to manage and no off-by-one mistakes.
foreach (string name in names)
{
    Console.WriteLine(name); // => Ana, then Ben, then Cleo
}

foreach fungerar med alla typer som C# känner igen som en sekvens, inklusive matriser, List<T>och Dictionary<TKey,TValue>. Iterationsvariabeln (name i föregående exempel) är skrivskyddad, så du kan inte omtilldela den i loopen.

Brödtexten i en loop är en enda instruktion, till exempel en tilldelning eller ett metodanrop. En block-instruktion är i sig en enda instruktion som omger noll eller fler instruktioner i klammerparenteser ({ }).

Många kodningsstandarder rekommenderar att du omger looptexten i klammerparenteser, även för en enda instruktion. Klamrar gör räckvidden explicit. Det förhindrar ett vanligt misstag: att man senare lägger till en andra rad som man förväntar sig ska köras vid varje iteration, men som i stället bara körs en gång efter loopen. Endast klammerparenteserna bestämmer vilka instruktioner som tillhör loopen. C# behandlar inte blanktecken som betydelsebärande, så enbart indrag har aldrig den funktionen. Klammerparenteser är lagliga även runt en rad: blocket är den enda instruktion som loopen upprepar. Indentera koden för läsbarhet, men förlita dig på klammerparenteser för att avgränsa blocket.

Upprepa medan ett villkor gäller med while

En while loop kontrollerar dess booleska villkor före varje iteration. Om villkoret är false i början körs aldrig brödtexten, så en while loop körs noll eller fler gånger:

int countdown = 3;

// A while loop checks its condition before each iteration, so the body
// runs zero or more times.
while (countdown > 0)
{
    Console.WriteLine(countdown); // => 3, then 2, then 1
    countdown--;
}

Kontrollera att något i loopen ändrar villkoret. I det föregående exemplet gör countdown-- till slut att villkoret blir false. En loop vars tillstånd aldrig blir false körs för evigt.

Kör brödtexten minst en gång med do-while

En do-while loop kontrollerar dess villkor efter varje iteration, så brödtexten körs alltid minst en gång. Använd det när det första passet måste ske innan du kan utvärdera villkoret, till exempel fråga efter indata och sedan verifiera det:

int attempts = 0;

// A do-while loop runs its body once, then checks the condition. Use it
// when the body must run at least one time.
do
{
    attempts++;
    Console.WriteLine($"Attempt {attempts}"); // => Attempt 1, then Attempt 2, then Attempt 3
}
while (attempts < 3);

Räkna med for

En for loop-instruktion innehåller tre delar: en initierare som körs en gång före loopen, ett villkor som kontrolleras före varje iteration och en iterator som körs efter varje iteration. Använd for när du behöver själva indexet, inte bara elementen. Vanligtvis behöver du indexet när du vill ändra elementet i stället för att läsa dess värde.

// A for loop fits when you need an explicit index. The three parts are
// the initializer, the condition, and the iterator.
for (int i = 0; i < 3; i++)
{
    Console.WriteLine(i); // => 0, then 1, then 2
}

När du bara läser elementen, när du inte använder positionerna eller tilldelar nya värden, föredrar du foreach. Den anger avsikten tydligare och undviker indexaritmetik.

Avsluta eller hoppa över med break och continue

Med två hoppinstruktioner får du finare kontroll i alla loopar. Instruktionen break avslutar loopen omedelbart och hoppar över eventuella återstående iterationer:

int[] numbers = [2, 4, 7, 8];

// break stops the loop immediately, skipping any remaining elements.
foreach (int number in numbers)
{
    if (number % 2 != 0)
    {
        Console.WriteLine($"First odd number: {number}"); // => First odd number: 7
        break;
    }
}

Instruktionen continue hoppar över resten av den aktuella iterationen och går vidare till nästa:

int[] values = [1, 2, 3, 4, 5];

// continue skips the rest of the current iteration and moves to the next.
foreach (int value in values)
{
    if (value % 2 == 0)
    {
        continue; // skip even numbers
    }

    Console.WriteLine(value); // => 1, then 3, then 5
}

Iterera en asynkron ström med await foreach

En asynkron ström är en läsare som använder en asynkron uppgift för att skapa varje nästa element. C# representerar den med IAsyncEnumerable<T> gränssnittet. Data som anländer över tid, till exempel sidor från ett webb-API eller rader från en databas, passar den här modellen: att hämta nästa element är en väntande åtgärd i stället för en omedelbar retur.

Om du vill använda en asynkron ström placerar du nyckelordet await före foreach. Varje iteration väntar på nästa element, så loopen pausas medan elementet skapas i stället för att blockera tråden:

private static async Task AwaitForeachExample()
{
    // await foreach consumes an asynchronous stream. Each iteration can
    // suspend while the next element is produced.
    await foreach (int value in GenerateAsync())
    {
        Console.WriteLine(value); // => 0, then 1, then 2
    }
}

private static async IAsyncEnumerable<int> GenerateAsync()
{
    for (int i = 0; i < 3; i++)
    {
        await Task.Delay(1); // stand-in for real asynchronous work
        yield return i;
    }
}

Asynkrona strömmar bygger på async och await. En fullständig genomgång finns i Generera och använda asynkrona strömmar.

Se även