Làm cách nào để kiểm tra xem IEnumerable là null hay rỗng?


154

Tôi yêu string.IsNullOrEmptyphương pháp. Tôi muốn có một cái gì đó cho phép chức năng tương tự cho IEnumerable. Có như vậy không? Có lẽ một số lớp người trợ giúp bộ sưu tập? Lý do tôi hỏi là trong các ifcâu lệnh, mã có vẻ lộn xộn nếu patter là (mylist != null && mylist.Any()). Nó sẽ sạch hơn nhiều để có Foo.IsAny(myList).

Bài đăng này không đưa ra câu trả lời: IEnumerable trống? .


1
@msarchet: Có lẽ tôi sẽ cho bạn câu trả lời nếu đây không phải là nhận xét :)
Schultz9999

Đối với tôi điều này có vẻ như là một vấn đề XY. thay vì hỏi "làm thế nào tôi có thể kiểm tra null chính xác ở mọi nơi mà không gây khó chịu", bạn nên hỏi "làm thế nào tôi có thể cải thiện thiết kế của mình để tôi KHÔNG phải kiểm tra null ở mọi nơi?"
sara

@nawfal, câu hỏi bạn liên kết đến không bao gồm kiểm tra null cụ thể, vì vậy tôi sẽ không coi đó là một bản sao
Mygeen

Câu trả lời:


188

Chắc chắn bạn có thể viết rằng:

public static class Utils {
    public static bool IsAny<T>(this IEnumerable<T> data) {
        return data != null && data.Any();
    }
}

tuy nhiên, hãy thận trọng rằng không phải tất cả các chuỗi đều có thể lặp lại; nói chung tôi thích chỉ đi bộ một lần, trong trường hợp.


12
Đây có phải là một mô hình tốt? Tôi sẽ bỏ thisở đó - Tôi coi các phương thức mở rộng được coi là được gọi nulllà dấu hiệu của thiết kế xấu.
Mormegil

28
@Mormegil Tại sao? các phương thức mở rộng cuối cùng cung cấp cho C # một số khả năng để hoạt động với null, mà các ngôn ngữ khác (như Ruby) hoàn toàn được cấp.
Matt Greer

5
Tại sao điều này nhất thiết là xấu? Như trong trường hợp này, đôi khi nó rất tiện dụng vì nó cho phép bạn xử lý mọi thứ đồng nhất hơn và với ít trường hợp đặc biệt hơn.
Ông Putty

5
@Mormegil meh - Tôi không thể hào hứng với điều đó. Miễn là ý định rõ ràng, v.v.
Marc Gravell

6
@Miryafa .Any()là một phương thức mở rộng hoạt động trên IEnumerable<T>(hoặc IQueryable<T>, mặc dù đó là một kịch bản khác). Làm như vậy sẽ tiêu tốn chuỗi , ít nhất là một phần (mặc dù điều đó vẫn có nghĩa là nó được tiêu thụ) - có thể chỉ cần đọc một yếu tố (đặc biệt là nếu không có vị ngữ). Như vậy, vì các chuỗi ( IEnumerable<T>) không cần phải lặp lại, nên có thể là như vậy . Any()không có một vị ngữ về cơ bản là tương đương với foreach(var x in sequence) { return true; } return false;- mặc dù nó sử dụng GetEnumerator()vv thay vì cú pháp trình biên dịch
Marc Gravell

119
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable) {
    return enumerable == null || !enumerable.Any();
}

8
tốt, không hoàn toàn, OP yêu cầu IEnumerable, không phải IEnumerable <T> ;-)
yoyo

8
Có, IEnumerablekhông có Any()phần mở rộng.
Blaise

23

Đây là phiên bản sửa đổi của câu trả lời hữu ích của @Matt Greer bao gồm lớp trình bao bọc tĩnh để bạn có thể sao chép-dán tệp này vào tệp nguồn mới, không phụ thuộc vào Linq và thêm IEnumerable<T>tình trạng quá tải chung , để tránh các loại giá trị Điều đó sẽ xảy ra với phiên bản không chung chung. [EDIT: Lưu ý rằng việc sử dụng IEnumerable<T>không ngăn cản quyền anh của điều tra viên, gõ vịt không thể ngăn chặn điều đó, nhưng ít nhất các yếu tố trong bộ sưu tập đánh giá trị sẽ không được đóng hộp.]

using System.Collections;
using System.Collections.Generic;

public static class IsNullOrEmptyExtension
{
    public static bool IsNullOrEmpty(this IEnumerable source)
    {
        if (source != null)
        {
            foreach (object obj in source)
            {
                return false;
            }
        }
        return true;
    }

    public static bool IsNullOrEmpty<T>(this IEnumerable<T> source)
    {
        if (source != null)
        {
            foreach (T obj in source)
            {
                return false;
            }
        }
        return true;
    }
}

15

Một cách khác là lấy Trình liệt kê và gọi phương thức MoveNext () để xem có mục nào không:

if (mylist != null && mylist.GetEnumerator().MoveNext())
{
    // The list is not null or empty
}

Điều này hoạt động cho IEnumerable cũng như IEnumerable <T>.


4
Bạn có nên gọi vứt bỏ trên điều tra viên này? Nếu bộ sưu tập là đa luồng nhận thức? Đúng. stackoverflow.com/questions/13459447/ cường
TamusJRoyce 10/03/2015

2
@TamusJRoyce Lưu ý rằng tuyên bố của bạn chỉ đúng với IEnumerable<T>, vì không chung chung IEnumerablekhông thực hiện IDisposable.
Ian Kemp

9

Cách tôi làm, tận dụng một số tính năng C # hiện đại:

Lựa chọn 1)

public static class Utils {
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> list) {
        return !(list?.Any() ?? false);
    }
}

Lựa chọn 2)

public static class Utils {
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> list) {
        return !(list?.Any()).GetValueOrDefault();
    }
}

Và nhân tiện, không bao giờ sử dụng Count == 0hoặc Count() == 0chỉ để kiểm tra nếu một bộ sưu tập trống. Luôn luôn sử dụng Linq's.Any()


2
Đếm == 0 là ổn .... Có thể nhanh hơn Any ()? Tuy nhiên, bạn đã đúng khi Đếm () == 0 là xấu. Đối với những người đang tự hỏi Count () lặp đi lặp lại trong toàn bộ bộ sưu tập của bạn, vì vậy nếu nó quá lớn có thể thêm một tấn chi phí!
Anthony Nichols

Đếm () chỉ lặp lại phép liệt kê nếu nó không thể được chuyển thành ICollection. Nói cách khác, khi bạn gọi phương thức này, nếu đã có thuộc tính Count trên đối tượng, nó sẽ trả về điều đó và hiệu suất sẽ giống hệt nhau. Kiểm tra việc thực hiện ở đây: referencesource.microsoft.com/#System.Core/System/Linq/...
Ronald Rey

Nếu bạn đang làm việc với IEnumerable, sử dụng Count () để kiểm tra độ trống chắc chắn là một ý tưởng tồi vì triển khai Linq S i lặp đi lặp lại trên toàn bộ bộ sưu tập, trong khi Any sẽ chỉ di chuyển trình lặp một lần. Hãy nhớ rằng bạn không thể sử dụng thuộc tính Count trong trường hợp này vì nó không phải là một phần của giao diện IEnumerable. Đó là lý do tại sao luôn luôn là một ý tưởng tốt hơn khi chỉ sử dụng Any () để kiểm tra độ trống trong tất cả các kịch bản, theo ý kiến ​​của tôi.
Ronald Rey

Ví dụ hay về cách toán tử phủ định không thể đọc !được, đặc biệt trong tùy chọn thứ hai;)
Fabio

6

Điều này có thể giúp

public static bool IsAny<T>(this IEnumerable<T> enumerable)
{
    return enumerable?.Any() == true;
}

public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
    return enumerable?.Any() != true;
}

5

Bắt đầu với C # 6, bạn có thể sử dụng tuyên truyền null :myList?.Any() == true

Nếu bạn vẫn thấy điều này quá tắc hoặc thích một phương pháp mở rộng tốt, tôi sẽ đề xuất câu trả lời của Matt Greer và Marc Gravell, nhưng với một chút chức năng mở rộng để hoàn thiện.

Câu trả lời của họ cung cấp các chức năng cơ bản giống nhau, nhưng mỗi người từ một quan điểm khác. Câu trả lời của Matt sử dụng tính string.IsNullOrEmptychất -mentality, trong khi câu trả lời của Marc đưa .Any()con đường của Linq hoàn thành công việc.

Cá nhân tôi có xu hướng sử dụng .Any()đường, nhưng muốn thêm chức năng kiểm tra điều kiện từ tình trạng quá tải khác của phương thức :

    public static bool AnyNotNull<T>(this IEnumerable<T> source, Func<T, bool> predicate = null)
    {
        if (source == null) return false;
        return predicate == null
            ? source.Any()
            : source.Any(predicate);
    }

Vì vậy, bạn vẫn có thể làm những việc như: myList.AnyNotNull(item=>item.AnswerToLife == 42);như bạn có thể với thường xuyên .Any()nhưng với kiểm tra null được thêm vào

Lưu ý rằng với cách C # 6: myList?.Any()trả về một bool?thay vì a bool, đó là hiệu ứng thực tế của việc truyền null


1
Vấn đề với bộ sưu tập? .Any () không phải là quá độ. Khi null, bộ sưu tập? .Any () == true là false, nhưng bộ sưu tập? .Any () == false cũng là false. Hơn nữa, bộ sưu tập? .Any () == false cũng sai ...
Jakub Szułakiewicz

4
if (collection?.Any() == true){
    // if collection contains more than one item
}
if (collection?.Any() != true){
    // if collection is null
    // if collection does not contain any item
}

2

Đây là mã từ câu trả lời của Marc Gravell , cùng với một ví dụ về việc sử dụng nó.

using System;
using System.Collections.Generic;
using System.Linq;

public static class Utils
{
    public static bool IsAny<T>(this IEnumerable<T> data)
    {
        return data != null && data.Any();
    }
}

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<string> items;
        //items = null;
        //items = new String[0];
        items = new String[] { "foo", "bar", "baz" };

        /*** Example Starts Here ***/
        if (items.IsAny())
        {
            foreach (var item in items)
            {
                Console.WriteLine(item);
            }
        }
        else
        {
            Console.WriteLine("No items.");
        }
    }
}

Như ông nói, không phải tất cả các chuỗi đều có thể lặp lại, do đó mã đôi khi có thể gây ra vấn đề, bởi vì IsAny()bắt đầu bước qua chuỗi. Tôi nghi ngờ câu trả lời của Robert Harvey có nghĩa là bạn thường không cần kiểm tra null bỏ trống. Thông thường, bạn chỉ có thể kiểm tra null và sau đó sử dụng foreach.

Để tránh bắt đầu chuỗi hai lần và tận dụng foreach, tôi chỉ viết một số mã như thế này:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        IEnumerable<string> items;
        //items = null;
        //items = new String[0];
        items = new String[] { "foo", "bar", "baz" };

        /*** Example Starts Here ***/
        bool isEmpty = true;
        if (items != null)
        {
            foreach (var item in items)
            {
                isEmpty = false;
                Console.WriteLine(item);
            }
        }
        if (isEmpty)
        {
            Console.WriteLine("No items.");
        }
    }
}

Tôi đoán phương thức mở rộng giúp bạn tiết kiệm một vài dòng gõ, nhưng mã này có vẻ rõ ràng hơn đối với tôi. Tôi nghi ngờ rằng một số nhà phát triển sẽ không ngay lập tức nhận ra rằng IsAny(items)sẽ thực sự bắt đầu bước qua chuỗi. (Tất nhiên nếu bạn đang sử dụng nhiều trình tự, bạn sẽ nhanh chóng học cách suy nghĩ về những bước qua chúng.)


Nếu bạn gọi IsAny bằng null, nó sẽ đưa ra một ngoại lệ
Ace Trajkov

3
Bạn đã thử chưa, @Ace? Có vẻ như nó sẽ đưa ra một ngoại lệ, nhưng các phương thức mở rộng có thể được gọi trong các trường hợp null .
Don Kirkby

2

Tôi sử dụng Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false);. Hi vọng điêu nay co ich.

Phá vỡ:

Collection?.Any()sẽ trả về nullnếu Bộ sưu tập là null và falsenếu Bộ sưu tập trống.

Collection?.Any()??falsesẽ cung cấp cho chúng tôi falsenếu Bộ sưu tập trống và falsenếu Bộ sưu tập là null.

Bổ sung điều đó sẽ cung cấp cho chúng tôi IsEmptyOrNull.


2

Anwser của Jon Skeet ( https://stackoverflow.com/a/28904021/8207463 ) có một cách tiếp cận tốt khi sử dụng Phương thức mở rộng - Bất kỳ () cho NULL và EMPTY. NHƯNG anh ấy đã xác nhận các câu hỏi trong trường hợp KHÔNG phải là NULL. Vì vậy, hãy cẩn thận thay đổi cách tiếp cận Jon Jons để xác thực AS NULL thành:

If (yourList?.Any() != true) 
{
     ..your code...
}

KHÔNG sử dụng (sẽ không xác nhận AS NULL):

If (yourList?.Any() == false) 
{
     ..your code...
}

Trong trường hợp bạn cũng có thể xác thực AS KHÔNG NULL (KHÔNG được kiểm tra như ví dụ nhưng không có lỗi trình biên dịch) làm một cái gì đó như sử dụng vị ngữ:

If (yourList?.Any(p => p.anyItem == null) == true) 
{
     ..your code...
}

https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,8788153112b7ffd0

Đối với phiên bản .NET nào bạn có thể sử dụng, vui lòng kiểm tra:

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any?view=netframework-4.8#moniker-applies-to


1

Tôi đã có cùng một vấn đề và tôi giải quyết nó như sau:

    public bool HasMember(IEnumerable<TEntity> Dataset)
    {
        return Dataset != null && Dataset.Any(c=>c!=null);
    }

"c => c! = null" sẽ bỏ qua tất cả các thực thể null.


1

Tôi đã xây dựng câu trả lời này bởi @Matt Greer

Anh ấy trả lời câu hỏi của OP một cách hoàn hảo.

Tôi muốn một cái gì đó như thế này trong khi duy trì các khả năng ban đầu của Any trong khi cũng kiểm tra null. Tôi đang đăng bài này trong trường hợp bất cứ ai khác cần một cái gì đó tương tự.

Cụ thể tôi muốn vẫn có thể vượt qua trong một vị ngữ.

public static class Utilities
{
    /// <summary>
    /// Determines whether a sequence has a value and contains any elements.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of source.</typeparam>
    /// <param name="source">The <see cref="System.Collections.Generic.IEnumerable"/> to check for emptiness.</param>
    /// <returns>true if the source sequence is not null and contains any elements; otherwise, false.</returns>
    public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source)
    {
        return source?.Any() == true;
    }

    /// <summary>
    /// Determines whether a sequence has a value and any element of a sequence satisfies a condition.
    /// </summary>
    /// <typeparam name="TSource">The type of the elements of source.</typeparam>
    /// <param name="source">An <see cref="System.Collections.Generic.IEnumerable"/> whose elements to apply the predicate to.</param>
    /// <param name="predicate">A function to test each element for a condition.</param>
    /// <returns>true if the source sequence is not null and any elements in the source sequence pass the test in the specified predicate; otherwise, false.</returns>
    public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        return source?.Any(predicate) == true;
    }
}

Việc đặt tên của phương thức mở rộng có thể tốt hơn.


0

Các giải pháp tốt nhất khác như dưới đây để kiểm tra trống hay không?

for(var item in listEnumerable)
{
 var count=item.Length;
  if(count>0)
  {
         // not empty or null
   }
  else
  {
       // empty
  }
}

1
Điều đó sẽ không hoạt động nếu listEnumerablelà null, đó là câu hỏi trong tầm tay
Timotei

0

Tôi sử dụng cái này:

    public static bool IsNotEmpty(this ICollection elements)
    {
        return elements != null && elements.Count > 0;
    }

Ejem:

List<string> Things = null;
if (Things.IsNotEmpty())
{
    //replaces ->  if (Things != null && Things.Count > 0) 
}

0

Vì một số tài nguyên đã cạn kiệt sau một lần đọc, tôi nghĩ tại sao không kết hợp giữa kiểm tra và đọc, thay vì kiểm tra riêng biệt truyền thống, sau đó đọc.

Đầu tiên chúng ta có một phần mở rộng nội tuyến check-for-null đơn giản hơn:

public static System.Collections.Generic.IEnumerable<T> ThrowOnNull<T>(this System.Collections.Generic.IEnumerable<T> source, string paramName = null) => source ?? throw new System.ArgumentNullException(paramName ?? nameof(source));

var first = source.ThrowOnNull().First();

Sau đó, chúng tôi có thêm một chút liên quan (ít nhất là cách tôi đã viết nó) kiểm tra phần mở rộng nội tuyến rỗng-và-rỗng:

public static System.Collections.Generic.IEnumerable<T> ThrowOnNullOrEmpty<T>(this System.Collections.Generic.IEnumerable<T> source, string paramName = null)
{
  using (var e = source.ThrowOnNull(paramName).GetEnumerator())
  {
    if (!e.MoveNext())
    {
      throw new System.ArgumentException(@"The sequence is empty.", paramName ?? nameof(source));
    }

    do
    {
      yield return e.Current;
    }
    while (e.MoveNext());
  }
}

var first = source.ThrowOnNullOrEmpty().First();

Tất nhiên bạn vẫn có thể gọi cả hai mà không cần tiếp tục chuỗi cuộc gọi. Ngoài ra, tôi đã bao gồm paramName, để người gọi có thể bao gồm một tên thay thế cho lỗi nếu đó không phải là "nguồn" đang được kiểm tra, ví dụ "nameof (đích)".


0
 public static bool AnyNotNull<TSource>(this IEnumerable<TSource> source)
    {
        return source != null && source.Any();
    }

phương pháp mở rộng của riêng tôi để kiểm tra Không null và Bất kỳ


0

Nếu không có người trợ giúp tùy chỉnh, tôi khuyên bạn nên ?.Any() ?? falsehoặc ?.Any() == truetương đối ngắn gọn và chỉ cần xác định trình tự một lần.


Khi tôi muốn xử lý một bộ sưu tập bị thiếu như một bộ trống, tôi sử dụng phương pháp mở rộng sau:

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

Hàm này có thể được kết hợp với tất cả các phương thức LINQ và foreach, không chỉ .Any(), đó là lý do tại sao tôi thích nó hơn các hàm trợ giúp chuyên biệt hơn mà mọi người đang đề xuất ở đây.


0

tôi sử dụng

    list.Where (r=>r.value == value).DefaultIfEmpty().First()

Kết quả sẽ là null nếu không khớp, nếu không thì trả về một trong các đối tượng

Nếu bạn muốn danh sách, tôi tin rằng việc rời First () hoặc gọi ToList () sẽ cung cấp danh sách hoặc null.



-1

chỉ cần thêm using System.Linqvà xem điều kỳ diệu xảy ra khi bạn cố gắng truy cập các phương thức có sẵn trong IEnumerable. Thêm điều này sẽ cung cấp cho bạn quyền truy cập vào phương thức có tên Count()đơn giản như vậy. chỉ cần nhớ kiểm tra null valuetrước khi gọi count():)


-1

Tôi đã sử dụng đơn giản nếu để kiểm tra nó

kiểm tra giải pháp của tôi

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
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.