A la lliçó anterior vas aprendre que el teu programa prengui decisions amb if/else, però
aquestes decisions s'avaluaven una sola vegada. A BiblioTechConsola, però, gairebé cap tasca
real consisteix a mirar una única dada: cal recórrer tot el catàleg de llibres per
mostrar-lo, comptar quants exemplars estan disponibles, o buscar-ne un pel títol fins a
trobar-lo (o comprovar que no existeix). Repetir una acció un nombre determinat o indeterminat
de vegades és la feina dels bucles. En aquesta lliçó coneixeràs les quatre formes de bucle
que ofereix C# —for, while, do-while i foreach— i aprendràs a controlar-los amb break
i continue.
Contingut
- El bucle
for - El bucle
while - El bucle
do-while - El bucle
foreach - Comparativa: quan usar cada un
breakicontinue- Bucles imbricats
- Aplicació completa: llistar, comptar i buscar al catàleg
- El bucle
for
forEl bucle for és ideal quan saps per endavant (o pots calcular) quantes vegades s'ha de
repetir una acció, típicament perquè recorres una col·lecció per índex.
string[] titolsLlibres = { "Cent anys de solitud", "Rayuela", "Ficcions", "El Aleph" };
for (int i = 0; i < titolsLlibres.Length; i++)
{
Console.WriteLine($"{i + 1}) {titolsLlibres[i]}");
}La capçalera del for té tres parts separades per ;:
| Part | Exemple | Quan s'executa |
|---|---|---|
| Inicialització | int i = 0 |
Una sola vegada, abans de començar |
| Condició | i < titolsLlibres.Length |
Abans de cada iteració; si és false, el bucle acaba |
| Increment (o actualització) | i++ |
Després de cada iteració |
Fer servir titolsLlibres.Length a la condició, en lloc d'un número fix com 4, evita que el
bucle deixi de funcionar (o falli) si l'array canvia de mida més endavant.
També es pot recórrer en sentit invers, cosa útil quan es van a eliminar elements d'una
col·lecció mentre es recorre (tema que s'ampliarà amb List<T> al Mòdul 4):
- El bucle
while
whilewhile repeteix un bloc mentre una condició sigui true, avaluant-la abans de cada
iteració. És l'opció natural quan no se sap per endavant quantes vegades caldrà repetir
l'acció.
int index = 0;
int llibresDisponibles = 0;
bool[] disponibilitat = { true, false, true, true };
while (index < disponibilitat.Length)
{
if (disponibilitat[index])
{
llibresDisponibles++;
}
index++;
}
Console.WriteLine($"Llibres disponibles: {llibresDisponibles}");Un cas típic de while és esperar una entrada vàlida de l'usuari, on no se sap quants intents
caldran:
string? entrada = null;
int paginesLlibre = 0;
bool entradaValida = false;
while (!entradaValida)
{
Console.Write("Introdueix el nombre de pagines del llibre: ");
entrada = Console.ReadLine();
entradaValida = int.TryParse(entrada, out paginesLlibre);
if (!entradaValida)
{
Console.WriteLine("Entrada no valida, torna-ho a intentar.");
}
}
Console.WriteLine($"Nombre de pagines registrat: {paginesLlibre}");Compte: si la condició d'un while mai no arriba a ser false, el bucle s'executa per sempre
(un bucle infinit). Sempre ha d'existir, dins del bloc, alguna instrucció que apropi la
condició al seu final (en el primer exemple, index++).
- El bucle
do-while
do-whiledo-while és gairebé idèntic a while, amb una diferència clau: la condició s'avalua
després d'executar el bloc, així que el bloc s'executa sempre almenys una vegada, fins
i tot si la condició ja era false des del principi.
int intents = 0;
bool prestecRegistrat = false;
do
{
intents++;
Console.WriteLine($"Intent numero {intents} de registrar el prestec...");
// Simulem que el registre te exit al segon intent
prestecRegistrat = intents >= 2;
} while (!prestecRegistrat);
Console.WriteLine("Prestec registrat correctament.");Fixa't en la sintaxi: la paraula while i la seva condició van després de la clau de
tancament, i la línia acaba amb ;. Aquest patró encaixa bé amb menús interactius, on el menú
s'ha de mostrar almenys una vegada abans de comprovar si l'usuari vol sortir.
- El bucle
foreach
foreachforeach recorre tots els elements d'una col·lecció (un array, o qualsevol tipus que
sigui "iterable", com veuràs amb més detall al Mòdul 4 amb les col·leccions), sense necessitat
de gestionar manualment un índex ni una condició d'aturada.
string[] titolsLlibres = { "Cent anys de solitud", "Rayuela", "Ficcions", "El Aleph" };
foreach (string titol in titolsLlibres)
{
Console.WriteLine(titol);
}foreach és més llegible que for quan no necessites l'índex, però té una limitació
important: la variable d'iteració (titol en l'exemple) és de només lectura dins del
bucle; no pots fer servir foreach per modificar els elements de l'array directament a través
d'ella.
int[] copiesDisponibles = { 2, 0, 1, 3 };
// foreach (int copies in copiesDisponibles)
// {
// copies++; // Error de compilacio: no es pot assignar a la variable de iteracio
// }
// Si necessites modificar aquest array, fes servir for amb index:
for (int i = 0; i < copiesDisponibles.Length; i++)
{
copiesDisponibles[i]++;
}
- Comparativa: quan usar cada un
| Bucle | S'utilitza quan... | Avalua la condició... |
|---|---|---|
for |
Saps (o pots calcular) el nombre d'iteracions, o necessites l'índex | Abans de cada iteració |
while |
No saps quantes iteracions faran falta, i podria ser zero | Abans de cada iteració |
do-while |
Com while, però el bloc s'ha d'executar almenys una vegada |
Després de cada iteració |
foreach |
Només necessites recórrer tots els elements, sense índex ni modificar-los | Abans de cada iteració (de forma interna) |
A la pràctica, for i foreach cobreixen la immensa majoria dels recorreguts sobre arrays i
col·leccions a BiblioTechConsola; while i do-while es reserven per a situacions on la
condició d'aturada depèn d'alguna cosa diferent de "recórrer una col·lecció completa", com
validar una entrada d'usuari o esperar que es compleixi una condició externa.
break i continue
break i continueDins de qualsevol bucle, dues paraules clau permeten alterar el flux normal d'iteració:
break: acaba el bucle immediatament, sense avaluar més iteracions.continue: salta la resta del cos del bucle en la iteració actual i passa directament a la següent.
string[] titolsLlibres = { "Cent anys de solitud", "Rayuela", "Ficcions", "El Aleph" };
string titolBuscat = "Ficcions";
for (int i = 0; i < titolsLlibres.Length; i++)
{
if (titolsLlibres[i] == titolBuscat)
{
Console.WriteLine($"Trobat a la posicio {i}.");
break; // ja no cal seguir recorrent aquest array
}
}bool[] disponibilitat = { true, false, true, true, false };
for (int i = 0; i < disponibilitat.Length; i++)
{
if (!disponibilitat[i])
{
continue; // saltem els llibres no disponibles
}
Console.WriteLine($"Llibre a la posicio {i}: disponible");
}break també funciona dins de while, do-while i foreach; continue, igualment.
- Bucles imbricats
Un bucle pot contenir un altre bucle al seu interior. Això és habitual quan es treballa amb arrays de dues dimensions (que ja vas veure a la lliçó d'arrays), per exemple, per representar un mapa de prestatgeries i lleixes a BiblioTech:
string[,] mapaPrestatgeries = new string[2, 3]
{
{ "Narrativa", "Narrativa", "Assaig" },
{ "Poesia", "Infantil", "Infantil" }
};
for (int prestatgeria = 0; prestatgeria < 2; prestatgeria++)
{
for (int lleixa = 0; lleixa < 3; lleixa++)
{
Console.WriteLine($"Prestatgeria {prestatgeria}, lleixa {lleixa}: {mapaPrestatgeries[prestatgeria, lleixa]}");
}
}El bucle exterior recorre les prestatgeries; per cada prestatgeria, el bucle interior recorre
les seves lleixes. En total s'executen 2 × 3 = 6 iteracions. Tingues en compte que break i
continue només afecten el bucle més intern en què es troben; si necessites sortir
d'ambdós bucles alhora, una tècnica senzilla és fer servir una variable booleana com a bandera:
bool trobat = false;
for (int prestatgeria = 0; prestatgeria < 2 && !trobat; prestatgeria++)
{
for (int lleixa = 0; lleixa < 3; lleixa++)
{
if (mapaPrestatgeries[prestatgeria, lleixa] == "Assaig")
{
Console.WriteLine($"Genere trobat a prestatgeria {prestatgeria}, lleixa {lleixa}");
trobat = true;
break;
}
}
}
- Aplicació completa: llistar, comptar i buscar al catàleg
Combinem tot l'anterior en tres operacions típiques sobre el catàleg de BiblioTechConsola: llistar els títols, comptar quants n'hi ha de disponibles i buscar-ne un pel títol.
string[] titolsLlibres = { "Cent anys de solitud", "Rayuela", "Ficcions", "El Aleph", "Pedro Paramo" };
bool[] disponibilitat = { true, false, true, true, false };
// 1) Llistar tots els titols amb el seu estat (foreach no serveix aqui perque cal saber la posicio)
Console.WriteLine("=== Cataleg BiblioTech ===");
for (int i = 0; i < titolsLlibres.Length; i++)
{
string estat = disponibilitat[i] ? "Disponible" : "Prestat";
Console.WriteLine($"{i + 1}) {titolsLlibres[i]} - {estat}");
}
// 2) Comptar quants llibres estan disponibles (foreach: no cal la posicio)
int totalDisponibles = 0;
foreach (bool estaDisponible in disponibilitat)
{
if (estaDisponible)
{
totalDisponibles++;
}
}
Console.WriteLine($"Total de llibres disponibles: {totalDisponibles}");
// 3) Buscar un titol concret i aturar-se en trobar-lo
string titolBuscat = "Ficcions";
bool trobat = false;
for (int i = 0; i < titolsLlibres.Length; i++)
{
if (titolsLlibres[i] == titolBuscat)
{
Console.WriteLine($"'{titolBuscat}' trobat a la posicio {i + 1}, disponible: {disponibilitat[i]}");
trobat = true;
break;
}
}
if (!trobat)
{
Console.WriteLine($"'{titolBuscat}' no es troba al cataleg.");
}Aquest fragment ja combina un for (quan l'índex importa), un foreach (quan només importa
el valor), i break (per aturar una cerca en trobar el resultat): tres decisions deliberades,
no arbitràries.
Errors Comuns i Consells
- Bucles infinits: oblidar actualitzar la variable de control en un
while(per exemple, no incrementar l'índex) deixa el programa penjat per sempre. Revisa sempre que la condició pugui arribar a serfalse. - Desbordar l'índex d'un array: en un
for, fer servir<=en lloc de<contraarray.Lengthprovoca accedir a una posició que no existeix i llança una excepció en temps d'execució (ho veuràs en detall a l'última lliçó del mòdul). - Intentar modificar la variable de
foreach: com s'ha vist, no compila. Si necessites modificar els elements, fes servirforamb índex. - Fer servir
forquanforeachés més clar (i viceversa): si no necessites l'índex, prefereixforeach; millora la llegibilitat i redueix el risc d'errors d'índex. - Confondre
breakambcontinue:breaksurt del bucle sencer;continuenomés salta a la iteració següent. Un ús equivocat sol produir resultats incomplets o bucles que acaben abans d'hora. - Imbricar bucles sense necessitat: abans d'imbricar dos bucles, comprova si el problema es pot resoldre amb un únic recorregut; la imbricació multiplica el nombre d'iteracions (i el temps d'execució) i afegeix complexitat.
Exercicis
-
Donat l'array
int[] copiesDisponibles = { 3, 0, 5, 2, 0 };, fes servir un bucleforper recórrer-lo i mostrar per consola quantes posicions tenen0còpies disponibles (llibres exhaurits). -
Fes servir un bucle
whileper simular la validació d'una contrasenya d'administrador: parteix destring intent = "";i d'una llista simulada d'intents vàlidsstring[] intentsSimulats = { "1234", "abcd", "bibliotech" };(recorre aquest array amb un índex creixent a cada volta delwhile, com si fossin intents successius de l'usuari). El bucle ha d'acabar en el moment queintentsigui igual a"bibliotech", mostrant quants intents han fet falta. -
Donat l'array de títols
string[] titolsLlibres = { "Cent anys de solitud", "Rayuela", "Ficcions", "El Aleph" };, recorre l'array ambforeachi, fent servircontinue, mostra per consola únicament els títols que tinguin més de 8 caràcters.
Solucions
int[] copiesDisponibles = { 3, 0, 5, 2, 0 };
int llibresExhaurits = 0;
for (int i = 0; i < copiesDisponibles.Length; i++)
{
if (copiesDisponibles[i] == 0)
{
llibresExhaurits++;
}
}
Console.WriteLine($"Llibres exhaurits: {llibresExhaurits}");
El resultat és 2, corresponent a les posicions 1 i 4 (índexs començant en 0).
string[] intentsSimulats = { "1234", "abcd", "bibliotech" };
string intent = "";
int index = 0;
int nombreIntents = 0;
while (intent != "bibliotech")
{
intent = intentsSimulats[index];
nombreIntents++;
index++;
}
Console.WriteLine($"Contrasenya correcta trobada despres de {nombreIntents} intent(s).");
El resultat mostra 3 intents, perquè "bibliotech" ocupa la tercera posició de l'array
simulat (índex 2). Fixa't que la condició del while s'avalua abans de cada volta, així
que l'última comprovació (intent != "bibliotech", ja false) és la que atura el bucle
sense executar una volta de més.
string[] titolsLlibres = { "Cent anys de solitud", "Rayuela", "Ficcions", "El Aleph" };
foreach (string titol in titolsLlibres)
{
if (titol.Length <= 8)
{
continue;
}
Console.WriteLine(titol);
}
Es mostra únicament "Cent anys de solitud" (20 caràcters). La resta de títols —
"Rayuela" (7 caràcters), "Ficcions" (8 caràcters) i "El Aleph" (8 caràcters)— tenen
una longitud igual o menor que 8, així que el continue salta les seves iteracions sense
mostrar res. És un bon exercici per comprovar amb Console.WriteLine(titol.Length) com
canvia el resultat si fas servir < en lloc de <=: amb <, tant "Ficcions" com
"El Aleph" (exactament 8 caràcters) deixarien de saltar-se i es mostrarien, ja que
8 < 8 és false.
Conclusió
En aquesta lliçó has après a repetir accions amb for, while i do-while, a recórrer
col·leccions de manera senzilla amb foreach, a controlar el flux d'un bucle amb break i
continue, i a combinar bucles imbricats quan les dades tenen més d'una dimensió. Amb
aquestes eines, BiblioTechConsola ja pot llistar el seu catàleg complet, comptar llibres
disponibles i buscar títols concrets sense repetir codi a mà.
Fins ara, per classificar alguna cosa (com l'estat d'un préstec) has fet servir cadenes de
if/else if/else. A la lliçó següent, Sentències Switch, coneixeràs una alternativa
més clara quan cal comparar una mateixa variable contra molts valors possibles, per exemple,
per classificar un llibre segons la seva categoria o gènere dins de BiblioTech.
Curs de Programació en C#
Mòdul 1: Introducció al C#
- Introducció al C#
- Configuració de l'Entorn de Desenvolupament
- Programa Hola Món
- Sintaxi i Estructura Bàsica
- Variables i Tipus de Dades
- Arrays i Cadenes de Text
Mòdul 2: Estructures de Control
Mòdul 3: Programació Orientada a Objectes
- Classes i Objectes
- Mètodes
- Constructors i Destructors
- Herència
- Polimorfisme
- Encapsulació
- Abstracció
- Structs i Records: Tipus per Valor i per Referència
Mòdul 4: Conceptes Avançats de C#
- Interfícies
- Delegats i Esdeveniments
- Pattern Matching i Característiques Modernes de C#
- Genèrics
- Col·leccions
- LINQ (Consulta Integrada en el Llenguatge)
- Programació Asíncrona
Mòdul 5: Treballant amb Dades
- Entrada/Sortida de Fitxers
- Serialització
- Connectivitat amb Bases de Dades
- Entity Framework
- Treball amb JSON i Consum d'APIs REST
Mòdul 6: Temes Avançats
- Reflexió
- Atributs
- Programació Dinàmica
- Gestió de Memòria i Recollida d'Escombraries
- Multifil i Programació Paral·lela
Mòdul 7: Construcció d'Aplicacions
Mòdul 8: Bones Pràctiques i Patrons de Disseny
- Estàndards de Codificació i Bones Pràctiques
- Patrons de Disseny
- Injecció de Dependències i Inversió de Control
- Proves Unitàries
- Revisió i Refactorització de Codi
