Có một singleton “Danh sách trống” trong C # không?


79

Trong C #, tôi sử dụng LINQ và IEnumerable một chút. Và tất cả đều tốt và tốt (hoặc ít nhất là gần như vậy).

Tuy nhiên, trong nhiều trường hợp, tôi thấy mình cần một khoảng trống IEnumerable<X>như một thứ mặc định. Đó là, tôi muốn

for (var x in xs) { ... }

để làm việc mà không cần kiểm tra null. Bây giờ đây là những gì tôi hiện đang làm, tùy thuộc vào bối cảnh lớn hơn:

var xs = f() ?? new X[0];              // when xs is assigned, sometimes
for (var x in xs ?? new X[0]) { ... }  // inline, sometimes

Bây giờ, mặc dù những điều trên hoàn toàn ổn đối với tôi - nghĩa là, nếu có bất kỳ "chi phí phụ" nào với việc tạo đối tượng mảng mà tôi không quan tâm - tôi đã tự hỏi:

Có singleton "IEnumerable / IList" trống bất biến trong C # /. NET không? (Và, ngay cả khi không, có cách nào "tốt hơn" để xử lý trường hợp được mô tả ở trên không?)

Java có Collections.EMPTY_LISTsingleton bất biến - "được đánh máy tốt" qua Collections.emptyList<T>()- phục vụ mục đích này, mặc dù tôi không chắc liệu một khái niệm tương tự có thể hoạt động trong C # hay không vì các generic được xử lý khác nhau.

Cảm ơn.


Chà, em yêu :) Đây là những gì tôi nhận được khi tập trung vào List / IList chứ không phải Enumerable / IEnumerable, cảm ơn tất cả - phiếu bầu xung quanh.


2
public static class Array<T> { public static readonly T[] Empty = new T[0]; }và nó có thể được gọi như sau: Array<string>.Empty. Tôi đã hỏi về nó ở đây trong CodeReview .
Şafak Gür

Câu trả lời:


97

Bạn đang tìm kiếm Enumerable.Empty<T>().

Trong các tin tức khác, danh sách trống của Java rất tệ bởi vì giao diện Danh sách hiển thị các phương thức để thêm các phần tử vào danh sách mà tạo ra các ngoại lệ.


Đó là một điểm rất hợp lệ! Có lẽ nó đảm bảo một câu hỏi SO mới?

1
Bạn đã chậm một vài giây và không có liên kết tài liệu * lắc ngón tay * ;-) Nhưng chấp nhận mở rộng khi nhận xét svicks.

Ngoài ra, những người khác cũng nhận được nhiều lượt ủng hộ hơn nên chúng tôi đồng đều :) Tôi sẽ đăng một câu hỏi nhưng tôi sẽ đi ngủ ngay bây giờ và tôi sẽ không thể theo dõi câu hỏi. Tại sao không đăng nó cho mình?
Stilgar

32
Vì một phương thức lớp có thể cần trả về a List(một tập hợp có thể được truy cập bằng chỉ mục). Chúng chỉ Listcó thể được đọc. Và đôi khi bạn cần trả về một giá trị chỉ đọc Listvới phần tử 0 trong đó. Như vậy, Collections.emptyList()phương pháp. Bạn không thể chỉ trả về một lặp có thể trống bởi vì một giao diện được triển khai chỉ định rằng phương thức trả về một danh sách. Vấn đề lớn là chúng không có giao diện ImmutableList mà bạn có thể quay lại. Vấn đề tương tự cũng tồn tại trong .NET: bất kỳ IListcũng có thể chỉ đọc được.
Laurent Bourgault-Roy

6
Một lưu ý nhỏ, mọi mảng đều là một IList <corresp Tương ứng> trong C # và điều này sẽ xuất hiện khi bạn cố gắng thêm các mục mới.
Grzenio

52

Enumerable.Empty<T>() chính xác là như vậy.


3
Chà, không chính xác . :) Vì một "danh sách" được yêu cầu, Array.Empty<T>()chính xác hơn, vì nó là một IList<T>. Nó có lợi ích hạnh phúc của việc thỏa mãn IReadOnlyCollection<T>. Tất nhiên, nơi một IEnumerable<T> không đủ, Enumerable.Empty<T>()phù hợp một cách hoàn hảo.
Timo

2
@Timo câu hỏi này, và nhiều câu trả lời bao gồm cả câu trả lời này, đã được viết khoảng 3 năm trước khi Array.Empty<T>có sẵn. :) Trong mọi trường hợp, bất kể tiêu đề, câu hỏi làm rõ ràng rằng an IEnumerable<T>là tốt cho mục đích.
Jon

19

Tôi nghĩ bạn đang tìm kiếm Enumerable.Empty<T>().

Singleton danh sách trống không có nhiều ý nghĩa như vậy, bởi vì danh sách thường có thể thay đổi.


12

Trong ví dụ ban đầu của bạn, bạn sử dụng một mảng trống để cung cấp một danh sách trống. Mặc dù việc sử dụng Enumerable.Empty<T>()là hoàn toàn đúng, nhưng có thể có các trường hợp khác: nếu bạn phải sử dụng một mảng (hoặc IList<T>giao diện), bạn có thể sử dụng phương pháp

System.Array.Empty<T>()

giúp bạn tránh phân bổ không cần thiết.

Ghi chú / Tài liệu tham khảo:


10

Tôi nghĩ rằng việc thêm một phương thức mở rộng là một giải pháp thay thế rõ ràng nhờ vào khả năng xử lý null của chúng - đại loại là:

  public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> list)
  {
    return list ?? Enumerable.Empty<T>();
  }

  foreach(var x in xs.EmptyIfNull())
  {
    ...
  }

1

Microsoft đã triển khai bất kỳ () 'như thế này ( nguồn )

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw new ArgumentNullException("source");
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        if (e.MoveNext()) return true;
    }
    return false;
}

Nếu bạn muốn lưu cuộc gọi trên ngăn xếp cuộc gọi, thay vì viết một phương thức mở rộng cuộc gọi !Any(), chỉ cần viết lại, thực hiện ba thay đổi sau:

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source) //first change (name)
{
    if (source == null) throw new ArgumentNullException("source");
    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        if (e.MoveNext()) return false; //second change
    }
    return true; //third change
}

1

Sử dụng Enumerable.Empty<T>()với danh sách có một hạn chế. Nếu bạn đưa Enumerable.Empty<T>vào hàm tạo danh sách thì một mảng có kích thước 4 sẽ được cấp phát. Nhưng nếu bạn đưa một sản phẩm trống Collectionvào hàm tạo danh sách thì không có phân bổ nào xảy ra. Vì vậy, nếu bạn sử dụng giải pháp này trong suốt mã của mình thì rất có thể một trong các IEnumerables sẽ được sử dụng để tạo danh sách, dẫn đến việc phân bổ không cần thiết.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.