Điều này là do thực tế, việc khởi tạo danh sách nội tuyến của bạn quá lớn cho ngăn xếp - xem này rấtrelated question over on the msdn forums trong trường hợp kịch bản này giống hệt nhau.
Phương thức trong .Net có cả chiều sâu và kích thước ngăn xếp. Một StackOverflowException
được gây ra không chỉ bởi số lượng các cuộc gọi trên ngăn xếp, nhưng kích thước tổng thể của mỗi phương pháp phân bổ bộ nhớ trong ngăn xếp. Trong trường hợp này, phương pháp của bạn chỉ là quá lớn - và đó là do số lượng biến cục bộ.
Bằng một ví dụ, hãy xem xét đoạn mã sau:
public class Foo
{
public int Bar { get; set;}
}
public Foo[] GetInts()
{
return new Foo[] { new Foo() { Bar = 1 }, new Foo() { Bar = 2 },
new Foo() { Bar = 3 }, new Foo() { Bar = 4 }, new Foo() { Bar = 5 } };
}
Bây giờ nhìn vào chì trong IL của phương pháp mà khi biên dịch (đây là một xây dựng phát hành, quá):
.maxstack 4
.locals init (
[0] class SomeExample/Foo '<>g__initLocal0',
[1] class SomeExample/Foo '<>g__initLocal1',
[2] class SomeExample/Foo '<>g__initLocal2',
[3] class SomeExample/Foo '<>g__initLocal3',
[4] class SomeExample/Foo '<>g__initLocal4',
[5] class SomeExample/Foo[] CS$0$0000
)
Lưu ý - bit thực tế trước /
, tức là SomeExample
sẽ phụ thuộc vào không gian tên và lớp trong đó phương thức và lớp lồng nhau được xác định - Tôi đã phải chỉnh sửa vài lần để loại bỏ các tên tệp khỏi mã tiến trình mà tôi 'tôi đang viết tại nơi làm việc!
Tại sao tất cả những người dân địa phương đó? Bởi vì cách khởi tạo nội tuyến được thực hiện.Mỗi đối tượng được tạo mới và được lưu trữ trong một địa chỉ 'ẩn', điều này là bắt buộc để việc gán thuộc tính có thể được thực hiện trên khởi tạo nội tuyến của mỗi Foo
(trường hợp đối tượng được yêu cầu để tạo thuộc tính được đặt cho Bar
). Điều này cũng thể hiện cách khởi tạo nội tuyến chỉ là một số đường cú pháp C#.
Trong trường hợp của bạn, đó là những người dân địa phương làm cho ngăn xếp phát nổ (có ít nhất một vài nghìn trong số đó chỉ dành cho các đối tượng cấp cao nhất - nhưng bạn cũng có các trình khởi tạo lồng nhau).
Trình biên dịch C# có thể cách tải trước số lượng tham chiếu cần thiết cho mỗi lần vào ngăn xếp (popping từng phần cho mỗi phân bổ thuộc tính) nhưng sau đó lạm dụng ngăn xếp nơi sử dụng người dân địa phương sẽ thực hiện nhiều tốt hơn.
Nó cũng có thể sử dụng một địa phương đơn, vì mỗi chỉ đơn thuần bằng văn bản-quá và sau đó được lưu trữ trong danh sách bằng chỉ số mảng, các địa phương không bao giờ cần nữa. Đó có thể là một cho đội C# để xem xét - Eric Lippert có thể có một vài suy nghĩ về điều này nếu anh ta tình cờ về chủ đề này.
Bây giờ, kiểm tra này cũng cho chúng ta một con đường tiềm năng xung quanh sử dụng này của người dân địa phương cho phương pháp rất lớn của bạn: sử dụng một iterator:
public Foo[] GetInts()
{
return GetIntsHelper().ToArray();
}
private IEnumerable<Foo> GetIntsHelper()
{
yield return new Foo() { Bar = 1 };
yield return new Foo() { Bar = 2 };
yield return new Foo() { Bar = 3 };
yield return new Foo() { Bar = 4 };
yield return new Foo() { Bar = 5 };
}
Bây giờ IL cho GetInts()
nay chỉ đơn giản là có .maxstack 8
tại đầu, và không có người dân địa phương. Nhìn vào chức năng lặp GetIntsHelper()
ta có:
.maxstack 2
.locals init (
[0] class SomeExample/'<GetIntsHelper>d__5'
)
Vì vậy, bây giờ chúng tôi đã ngừng sử dụng tất cả những người dân địa phương trong các phương pháp đó ...
Nhưng ...
Nhìn vào lớp SomeExample/'<GetIntsHelper>d__5'
, đã được trình biên dịch tạo tự động - và chúng tôi thấy rằng người dân địa phương vẫn ở đó - họ vừa được thăng cấp lên các trường trên lớp đó:
.field public class SomeExample/Foo '<>g__initLocal0'
.field public class SomeExample/Foo '<>g__initLocal1'
.field public class SomeExample/Foo '<>g__initLocal2'
.field public class SomeExample/Foo '<>g__initLocal3'
.field public class SomeExample/Foo '<>g__initLocal4'
Vì vậy, câu hỏi đặt ra là - liệu việc tạo đối tượng đó cũng có thổi bay ngăn xếp nếu áp dụng cho kịch bản của bạn không? Có lẽ là không, bởi vì trong bộ nhớ nó nên giống như cố gắng khởi tạo mảng lớn - nơi mà các mảng triệu phần tử là khá chấp nhận được (giả sử đủ bộ nhớ trong thực tế).
Vì vậy - bạn có thể có thể sửa chữa mã của bạn khá đơn giản bằng cách di chuyển sang sử dụng một phương pháp IEnumerable
rằng yield
s mỗi phần tử. Tuy nhiên, thực hành tốt nhất nói rằng nếu bạn hoàn toàn phải có định nghĩa tĩnh này - hãy xem xét việc thêm dữ liệu vào tài nguyên hoặc tệp được nhúng trên đĩa (XML và LINQ to XML có thể là một lựa chọn tốt) và sau đó tải nó từ đó nhu cầu.
Vẫn còn tốt hơn - dán vào cơ sở dữ liệu :)
Bạn đang truy cập danh sách như thế nào? – Oded
JEEEEZ, bạn thực sự nên xem xét việc lưu trữ chúng ở nơi khác, mã hóa cứng hơn 3500 mục! : | – mattytommo
@Oded - giống như var seller = DataHelper.Sellers; – Gaz