Trong C#, cach thong dung nhat de lap qua mot danh sach la foreach. Tuy nhien, khi can biet chi so, xoa phan tu, thay doi du lieu hoac toi uu hieu nang, ban se can them mot so lua chon khac.
Neu dang can cach don gian nhat, day la tat ca nhung gi ban can:
List<string> fruits = ["apple", "banana", "cherry"];
foreach (var fruit in fruits)
{
Console.WriteLine(fruit);
}
Bai viet nay trinh bay cac cach thuc te de lap qua List<T>: foreach voi break va continue, vong lap for, lay chi so, List<T>.ForEach, lap nguoc, thay doi danh sach trong khi lap, danh sach long nhau, Span<T> va xu ly bat dong bo. Cuoi bai la ket qua benchmark tren .NET 10 va .NET 11.
Mục lục
Lap qua List voi foreach
Hay uu tien cau lenh foreach. Cu phap ngan gon, ban khong phai tu quan ly chi so va no hoat dong voi moi collection, khong chi rieng List<T>.
List<string> fruits = ["apple", "banana", "cherry"];
foreach (var fruit in fruits)
{
Console.WriteLine(fruit);
}
foreachkhong chi danh choIEnumerable<T>. Bat ky kieu nao co phuong thuc cong khaiGetEnumerator()deu co the dung voiforeach. Do do, ban cung co the lap quaSpan<T>.- Danh sach
nullse gay loi. Lap quanulltao raNullReferenceException. Danh sach rong thi hop le, nhung than vong lap se khong chay. - Bien lap chi de doc. Ban khong the viet
fruit = "mango";trong than vong lap. Neu can thay the phan tu, hay dungfor.
Su dung break va continue trong foreach
Dung continue de bo qua phan con lai cua lan lap hien tai va chuyen sang phan tu tiep theo. Dung break de thoat khoi vong lap ngay lap tuc.
List<string> fruits = ["apple", "banana", "cherry", "date"];
foreach (var fruit in fruits)
{
if (fruit == "banana")
continue;
if (fruit == "date")
break;
Console.WriteLine(fruit);
}
Ket qua la apple va cherry. Hai tu khoa nay cung hoat dong trong for, while va do. Neu co vong lap long nhau, break chi thoat khoi vong lap gan nhat.
Lap qua List voi vong lap for
Cau lenh for cho ban vi tri cua tung phan tu. Hay dung no khi can chi so hoac muon thay the phan tu trong danh sach.
for (var i = 0; i < fruits.Count; i++)
{
Console.WriteLine($"{i}: {fruits[i]}");
}
Vi truy cap truc tiep qua chi so, ban co the thay doi phan tu tai cho:
for (var i = 0; i < fruits.Count; i++)
{
fruits[i] = fruits[i].ToUpperInvariant();
}
// APPLE, BANANA, CHERRY
Voi List<T>, hay dung thuoc tinh Count thay vi phuong thuc LINQ Count(). Ca hai cung tra ve so luong phan tu, nhung Count doc gia tri da duoc luu san, con Count() la phuong thuc mo rong LINQ va phai kiem tra kieu cua collection truoc.
Neu lap qua mang, hay dung Length thay cho Count. Cac cach con lai trong bai viet cung ap dung cho mang.
Lay chi so trong vong lap foreach
Neu thich cach doc cua foreach nhung van can chi so, .NET 9 bo sung Enumerable.Index(). Phuong thuc nay ket hop moi phan tu voi vi tri cua no trong mot tuple co the tach thanh bien:
foreach (var (index, fruit) in fruits.Index())
{
Console.WriteLine($"{index}: {fruit}");
}
Tren cac phien ban .NET cu hon, ban co the dung phien ban Select co chi so:
foreach (var (fruit, index) in fruits.Select((f, i) => (f, i)))
{
Console.WriteLine($"{index}: {fruit}");
}
Lap qua List voi List.ForEach
List<T> co phuong thuc ForEach rieng, nhan vao mot Action<T>. Vi day la phuong thuc cua List<T>, no khong co tren IEnumerable<T> hoac mang. LINQ cung khong co phuong thuc ForEach; voi IEnumerable<T>, hay dung foreach.
fruits.ForEach(fruit => Console.WriteLine(fruit));
// Hoac dung method group
fruits.ForEach(Console.WriteLine);
Cach nay phu hop cho cac thao tac mot dong, nhung khong nen lam lua chon mac dinh. Ban khong the dung break de thoat khoi no, va no gay van de voi async. Khi truyen lambda async vao ForEach, trinh bien dich bien no thanh phuong thuc async void. ForEach khong the cho ket qua do, vi vay chuong trinh co the chuyen tiep truoc khi cong viec hoan tat.
Tai lieu cua Microsoft cung luu y rang viec thay doi danh sach ben trong delegate la khong duoc ho tro va co the dan den hanh vi khong xac dinh.
Lap qua List theo chieu nguoc
Cach ro rang nhat de lap tu cuoi danh sach ve dau la dung for dem lui:
for (var i = fruits.Count - 1; i >= 0; i--)
{
Console.WriteLine(fruits[i]);
}
Ban cung co the dung LINQ Enumerable.Reverse. Cach nay giu nguyen danh sach goc:
foreach (var fruit in Enumerable.Reverse(fruits))
{
Console.WriteLine(fruit);
}
Hay can than voi fruits.Reverse(). Tren List<T>, loi goi nay uu tien phuong thuc instance, phuong thuc se dao nguoc danh sach ngay tai cho va tra ve void. Vi vay, no khong the dung trong foreach; neu goi tren mot dong rieng, ban da thay doi thu tu du lieu.
Neu chi can phan tu cuoi cung, dung toan tu chi so C# 8: fruits[^1].
Thay doi List trong khi lap
Neu xoa phan tu khoi danh sach ben trong foreach, chuong trinh co the bien dich nhung se nem InvalidOperationException o lan lap tiep theo:
List<int> numbers = [1, 2, 3, 4, 5, 6];
foreach (var n in numbers)
{
if (n % 2 == 0)
numbers.Remove(n);
}
Loi co thong bao: Collection was modified; enumeration operation may not execute.
Enumerator cua danh sach theo doi mot so phien ban. Moi lan Add hoac Remove duoc goi, so phien ban tang len. Lan goi MoveNext tiep theo phat hien thay doi va nem loi.
Dung RemoveAll
Neu dang loc phan tu, RemoveAll la lua chon gon nhat. Phuong thuc nhan vao predicate va tra ve so phan tu da xoa.
var removed = numbers.RemoveAll(n => n % 2 == 0);
// Da xoa 3 phan tu: 2, 4, 6
Lap nguoc voi for
Khi xoa mot phan tu, cac phan tu phia sau se dich xuong mot chi so. Neu lap tu cuoi len dau, cac phan tu bi dich la nhung phan tu ban da kiem tra, vi vay khong bi bo sot:
for (var i = numbers.Count - 1; i >= 0; i--)
{
if (numbers[i] % 2 == 0)
numbers.RemoveAt(i);
}
Lap qua mot ban sao
Goi ToList() tao ra ban sao danh sach. Khi do, ban co the them hoac xoa phan tu trong danh sach goc ma khong gay loi:
foreach (var n in numbers.ToList())
{
if (n % 2 == 0)
numbers.Remove(n);
}
Doi lai, cach nay tao them bo nho va phai sao chep toan bo danh sach.
Thay doi thuoc tinh cua cac doi tuong trong foreach thi khong sao. Khi danh sach chua cac kieu tham chieu, ban dang thay doi doi tuong ma danh sach tro den, khong thay doi cau truc danh sach:
foreach (var product in products)
{
product.Price *= 1.1m;
}
Lap qua danh sach long nhau va lam phang du lieu
Voi cac danh sach long nhau, hay long cac vong lap:
List<List<int>> grid = [[1, 2, 3], [4, 5], [6]];
foreach (var row in grid)
{
foreach (var cell in row)
{
Console.Write($"{cell} ");
}
Console.WriteLine();
}
Neu khong quan tam den tung hang va chi muon lay tat ca phan tu, SelectMany se lam phang cau truc:
foreach (var cell in grid.SelectMany(row => row))
{
Console.Write($"{cell} ");
}
// 1 2 3 4 5 6
Lap qua List voi Span
Trong cac doan ma chay rat nhieu lan, CollectionsMarshal.AsSpan cung cap mot Span<T> tro den mang noi bo cua danh sach. Cach nay bo qua viec kiem tra phien ban cua enumerator va lap truc tiep tren mang:
using System.Runtime.InteropServices;
foreach (var score in CollectionsMarshal.AsSpan(scores))
{
Console.WriteLine(score);
}
Span<T> con cho phep lap qua bang ref, nhung ban co the thay doi phan tu ngay tai cho, ke ca truong cua struct:
List<int> scores = [10, 20, 30];
foreach (ref var score in CollectionsMarshal.AsSpan(scores))
{
score += 5;
}
// 15, 25, 35
Tai lieu khuyen cao khong them hoac xoa phan tu trong khi span dang duoc su dung. Span khong co co che kiem tra phien ban. Neu danh sach mo rong va cap phat lai mang noi bo, span van tro den mang cu. Khi do, viec doc co the bo sot phan tu moi va viec ghi co the chi thay doi mang cu thay vi danh sach. Chuong trinh khong nhat thiet nem loi.
Chi nen dung cach nay trong ma nhay cam voi hieu nang, noi ban kiem soat duoc vong doi cua danh sach.
Lap qua List theo cach bat dong bo
Co ba kieu lap bat dong bo, moi kieu giai quyet mot nhu cau khac nhau.
Dung foreach voi await
Neu ban da co mot danh sach va can await mot thao tac cho tung phan tu, hay dung foreach thong thuong va dat await ben trong. Moi lan lap se hoan tat truoc khi lan tiep theo bat dau:
foreach (var url in urls)
{
var result = await FetchAsync(url);
Console.WriteLine(result);
}
Dung await foreach
Neu du lieu den theo cach bat dong bo duoi dang IAsyncEnumerable<T>, hay dung await foreach:
await foreach (var line in ReadLinesAsync())
{
Console.WriteLine(line);
}
Dung Parallel.ForEachAsync
Neu cac phan tu khong phu thuoc lan nhau va ban muon xu ly chung dong thoi, hay dung Parallel.ForEachAsync. Phuong thuc nay chay lambda bat dong bo voi nhieu phan tu cung luc va chi ket thuc khi tat ca da hoan tat.
await Parallel.ForEachAsync(
urls,
new ParallelOptions { MaxDegreeOfParallelism = 4 },
async (url, cancellationToken) =>
{
var result = await FetchAsync(url, cancellationToken);
Console.WriteLine(result);
});
Su dung MaxDegreeOfParallelism de gioi han so tac vu chay cung luc. Thu tu dau ra co the thay doi sau moi lan chay.
Khong truyen lambda async vao List<T>.ForEach. Ban khong the cho no va vong lap co the ket thuc truoc khi cong viec bat dong bo hoan thanh.
Benchmark: Cach lap nao nhanh nhat?
De so sanh for va foreach, cac cach lap duoc do bang BenchmarkDotNet voi MemoryDiagnoser. Bai do tinh tong mot List<int> co 10, 1.000 va 100.000 phan tu tren .NET 10.0.12 va .NET 11 RC 1.
Ket qua voi 100.000 phan tu:
for: 34,70 micro giay tren .NET 10 va 35,49 micro giay tren .NET 11.foreach: 35,18 va 34,74 micro giay.List.ForEach: 51,93 va 51,77 micro giay, cap phat 24 byte.Index(): 196,39 va 194,84 micro giay, cap phat 104 byte.AsSpan: 27,27 va 27,19 micro giay.
Doan benchmark tieu bieu:
[MemoryDiagnoser]
public class LoopBenchmarks
{
private List<int> _numbers = [];
[Params(10, 1_000, 100_000)]
public int Size { get; set; }
[GlobalSetup]
public void Setup() => _numbers = Enumerable.Range(1, Size).ToList();
[Benchmark(Baseline = true)]
public long For()
{
long sum = 0;
for (var i = 0; i < _numbers.Count; i++)
sum += _numbers[i];
return sum;
}
[Benchmark]
public long Foreach()
{
long sum = 0;
foreach (var number in _numbers)
sum += number;
return sum;
}
[Benchmark]
public long Span()
{
long sum = 0;
foreach (var number in CollectionsMarshal.AsSpan(_numbers))
sum += number;
return sum;
}
}
Ket luan tu benchmark
forvaforeachgan nhu ngang nhau. Voi 1.000 va 100.000 phan tu, thoi gian chenh lech duoi 2 phan tram tren ca hai runtime. JIT toi uuforeachtrenList<T>rat tot, vi vay hay chon cach de doc hon.AsSpannhanh nhat. Tu 1.000 phan tu tro len, no nhanh honforkhoang 21 den 26 phan tram do lap truc tiep tren mang nen va bo qua mot so kiem tra gioi han.List.ForEachcham hon khoang 1,5 lan. Moi phan tu phat sinh mot lan goi delegate. Lambda trong benchmark con giu biensum, tao ra closure 24 byte.Index()cham hon khoang 5,6 lan va cap phat 104 byte. No di qua iterator cua LINQ thay vi struct enumerator cua danh sach. Phu hop cho ma thong thuong, nhung nen tranh trong vong lap nong.- .NET 11 co hieu nang gan nhu .NET 10. Voi 1.000 va 100.000 phan tu, moi cach deu nam trong khoang 4 phan tram so voi .NET 10. Khong nen thay doi ma chi vi chenh lech vai nano giay o kich thuoc 10 phan tu.
Cau hoi thuong gap
Lam sao de lap qua mot danh sach trong C#?
Dung foreach (var item in list) { ... }. Chuyen sang for khi can chi so hoac muon thay the phan tu.
Lam sao thoat khoi vong lap foreach?
Dung break de thoat ngay, hoac continue de chuyen sang phan tu tiep theo. Hai tu khoa nay khong dung duoc ben trong List<T>.ForEach.
Trong C#, for hay foreach nhanh hon?
Voi List<T>, hai cach gan nhu co cung toc do. Hay dung foreach neu khong can chi so; dung CollectionsMarshal.AsSpan trong doan ma nong khi can them khoang 20 phan tram hieu nang.
Lam sao lay chi so trong foreach?
Tren .NET 9 tro len, dung foreach (var (index, item) in list.Index()). Tren phien ban cu hon, dung list.Select((item, index) => (item, index)).
Lam sao sua loi Collection was modified?
Loi nay xuat hien khi them hoac xoa phan tu trong foreach. Hay dung list.RemoveAll(predicate), lap nguoc voi for va RemoveAt(i), hoac lap tren ban sao tao boi list.ToList().
Lap qua mang trong C# co giong List khong?
Co. foreach hoat dong tuong tu. Neu dung for, hay dung array.Length thay cho list.Count.
Bonus: Lap vong quanh List
Doi khi ban khong muon vong lap dung lai o phan tu cuoi. Bo chuyen anh, danh sach phat nhac va bo dieu phoi luan phien deu can quay lai phan tu dau tien. Toan tu chia lay du, hay modulo, la cach pho bien de xu ly truong hop nay.
Ket luan
foreach: doc tung phan tu; day la lua chon mac dinh.for: khi can chi so hoac can thay the phan tu.Index(): khi muon giu cach doc cuaforeachva co them chi so, tu .NET 9.RemoveAllhoacforlap nguoc: khi can xoa phan tu.CollectionsMarshal.AsSpan: cho cac doan ma nong, noi ban kiem soat duoc danh sach.await foreach: choIAsyncEnumerable<T>;Parallel.ForEachAsynccho cong viec dong thoi.- Khong dung lambda
asyncben trongList<T>.ForEach.
Tom lai, hay chon cach lap de doc va bao tri nhat. Chi dung toi uu hoa bang Span<T> khi benchmark cho thay doan ma thuc su can them hieu nang.



