Foreach có thể gây ra phân bổ, nhưng ít nhất là trong phiên bản mới hơn .NET và Mono, nó không nếu bạn đang xử lý các loại hoặc mảng cụ thể System.Collections.Generic
. Các phiên bản cũ hơn của các trình biên dịch này (chẳng hạn như phiên bản Mono được Unity3D sử dụng cho đến 5.5) luôn tạo ra phân bổ.
Trình biên dịch C# sử dụng duck typing để tìm phương thức GetEnumerator()
và sử dụng nếu có thể. Hầu hết các phương thức GetEnumerator()
trên các kiểu System.Collection.Generic
có phương thức GetEnumerator() trả về cấu trúc và mảng được xử lý đặc biệt. Nếu phương pháp GetEnumerator()
của bạn không phân bổ, bạn thường có thể tránh phân bổ.
Tuy nhiên, bạn sẽ luôn nhận được phân bổ nếu bạn đang xử lý một trong các giao diện IEnumerable
, IEnumerable<T>
, IList
hoặc IList<T>
. Ngay cả khi lớp triển khai của bạn trả về cấu trúc, cấu trúc sẽ được đóng hộp và truyền tới IEnumerator
hoặc IEnumerator<T>
, yêu cầu phân bổ.
Chú ý: Vì Unity 5,5 cập nhật để C# 6, tôi biết không phát hành biên dịch hiện nay mà vẫn có phân bổ thứ hai này.
Có phân bổ thứ hai phức tạp hơn một chút để hiểu. Đi vòng lặp foreach này:
List<int> valueList = new List<int>() { 1, 2 };
foreach (int value in valueList) {
// do something with value
}
Cho đến C# 5.0, nó mở rộng đến một cái gì đó như thế này (với một số khác biệt nhỏ):
List<int>.Enumerator enumerator = valueList.GetEnumerator();
try {
while (enumerator.MoveNext()) {
int value = enumerator.Current;
// do something with value
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
Trong khi List<int>.Enumerator
là một cấu trúc, và không cần phải được phân bổ trên heap, các cast enumerator as System.IDisposable
hộp struct, đó là một phân bổ. Thông số được thay đổi bằng C# 5.0, cấm phân bổ, nhưng .NET broke the spec và tối ưu hóa phân bổ trước đó.
Các phân bổ này cực kỳ nhỏ. Lưu ý rằng phân bổ rất khác với rò rỉ bộ nhớ, và với bộ sưu tập rác, bạn thường không phải lo lắng về nó. Tuy nhiên, có một số tình huống khi bạn quan tâm đến cả những phân bổ này. Tôi làm Unity3D làm việc và cho đến 5.5, chúng tôi không thể có bất kỳ phân bổ trong hoạt động xảy ra mỗi khung trò chơi bởi vì khi thu gom rác chạy, bạn nhận được một lurch đáng chú ý.
Lưu ý rằng vòng lặp foreach trên mảng được xử lý đặc biệt và không phải gọi Dispose. Vì vậy, theo như tôi có thể nói, foreach chưa bao giờ được phân bổ khi lặp qua mảng.
a) Đây là chi tiết triển khai. Đó là những thứ không liên quan. b) GC tồn tại. –
Tôi đoán, vấn đề chính với Enumerator đơn sẽ là an toàn luồng. – volpav
Điều này không giới hạn đối với 'foreach()'. Toàn bộ ý tưởng về việc có một GC là những vật thể nhỏ, sống ngắn rất rẻ. Chúng ta luôn lãng phí chúng, như trong 'text = text.ToLower();'. Đừng lo lắng về điều đó. Hầu hết các chương trình tái sử dụng hóa ra tốn kém hơn. –