จะตรวจสอบว่า IEnumerable เป็นโมฆะหรือว่างเปล่าได้อย่างไร?


155

ฉันรักstring.IsNullOrEmptyวิธีการ ฉันชอบที่จะมีบางสิ่งบางอย่างที่จะช่วยให้การทำงานเดียวกันสำหรับ IEnumerable มีแบบนี้เหรอ? บางทีคลาสตัวช่วยในการรวบรวมข้อมูล เหตุผลที่ผมถามคือว่าในงบลักษณะรหัสรกถ้าลายคือif (mylist != null && mylist.Any())มันจะสะอาดFoo.IsAny(myList)กว่าที่คิด

โพสต์นี้ไม่ได้ให้คำตอบว่า: IEnumerable ว่างเปล่า? .


1
@msarchet: ผมอาจจะให้คำตอบว่านี้ไม่ได้แสดงความคิดเห็น :)
Schultz9999

สำหรับฉันนี่ดูเหมือนจะเป็นปัญหา XY ชนิดหนึ่ง แทนที่จะถามว่า "ฉันจะตรวจสอบโมฆะได้ทุกที่โดยไม่น่ารำคาญ" คุณควรถามว่า "ฉันจะปรับปรุงการออกแบบได้อย่างไรฉันจะไม่ต้องตรวจสอบ null ทุกที่"
sara

@nawfal คำถามที่คุณเชื่อมโยงไปยังไม่รวมถึงการตรวจสอบโมฆะโดยเฉพาะดังนั้นฉันจะไม่คิดว่ามันซ้ำ
Mygeen

คำตอบ:


188

แน่นอนคุณสามารถเขียนว่า:

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

อย่างไรก็ตามโปรดใช้ความระมัดระวังว่าลำดับทั้งหมดไม่สามารถทำซ้ำได้ โดยทั่วไปฉันต้องการเดินเพียงครั้งเดียวในกรณี


12
นี่เป็นรูปแบบที่ดีหรือไม่? ฉันจะวางที่thisนั่น - ฉันพิจารณาวิธีการขยายที่สันนิษฐานว่าจะเรียกว่าnullเป็นสัญลักษณ์ของการออกแบบที่น่าเกลียด
Mormegil

28
@Memegil ทำไม? ในที่สุดวิธีการขยายให้ C # ความสามารถบางอย่างในการทำงานกับ nulls ซึ่งภาษาอื่น ๆ (เช่น Ruby) ได้รับอนุญาตอย่างสมบูรณ์
Matt Greer

5
นี่คือเหตุผลที่จำเป็นต้องไม่ดี? ในกรณีนี้บางครั้งมันมีประโยชน์มากเพราะช่วยให้คุณรักษาสิ่งต่างๆให้เป็นเนื้อเดียวกันและมีกรณีพิเศษน้อยลง
Mr. Putty

5
@Memegil meh - ฉันไม่สามารถตื่นเต้นเกี่ยวกับเรื่องนี้ ตราบใดที่ความตั้งใจนั้นชัดเจน ฯลฯ
Marc Gravell

6
@Miryafa .Any()เป็นวิธีส่วนขยายที่ทำงานบนIEnumerable<T>(หรือIQueryable<T>แม้ว่าจะเป็นสถานการณ์อื่น) การทำเช่นนี้กินลำดับอย่างน้อยบางส่วน (แม้ว่าจะยังคงหมายถึงการบริโภค) - มันอาจจำเป็นต้องอ่านองค์ประกอบหนึ่งเท่านั้น (โดยเฉพาะถ้าไม่มีเพรดิเคต) เป็นเช่นนี้มาตั้งแต่ลำดับ ( IEnumerable<T>) ไม่ได้จะต้องมีการทำซ้ำที่อาจจะเป็น Any()โดยไม่ต้องวินิจฉัยเป็นหลักเทียบเท่ากับforeach(var x in sequence) { return true; } return false;- แม้ว่าจะใช้GetEnumerator()ฯลฯ แทนไวยากรณ์คอมไพเลอร์
มาร์ค Gravell

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

8
ดีไม่มาก OP ขอ IEnumerable ไม่ใช่ IEnumerable <T> ;-)
yoyo

8
ใช่IEnumerableไม่มีAny()ส่วนขยาย
Blaise

23

ต่อไปนี้เป็นคำตอบที่มีประโยชน์ของ @Matt Greer ที่ได้รับการแก้ไขซึ่งรวมคลาส wrapper แบบคงที่ดังนั้นคุณสามารถคัดลอกวางลงในไฟล์ต้นฉบับใหม่ไม่ได้ขึ้นอยู่กับ Linq และเพิ่มIEnumerable<T>โอเวอร์โหลดทั่วไปเพื่อหลีกเลี่ยงการต่อสู้ประเภทค่า ที่จะเกิดขึ้นกับรุ่นที่ไม่ใช่ทั่วไป [แก้ไข: โปรดทราบว่าการใช้งานIEnumerable<T>ไม่ได้ป้องกันการชกมวยของตัวแจงนับการพิมพ์เป็ดไม่สามารถป้องกันได้ แต่อย่างน้อยองค์ประกอบในคอลเลกชันที่พิมพ์ตามตัวอักษรจะไม่ถูกบรรจุในกล่อง]

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

อีกวิธีหนึ่งคือรับ Enumerator และเรียกใช้เมธอด MoveNext () เพื่อดูว่ามีรายการใด ๆ หรือไม่:

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

สิ่งนี้ใช้ได้กับ IEnumerable และ IEnumerable <T>


4
คุณควรจะจัดการกับตัวแจงนับนี้หรือไม่? หากการรวบรวมแบบมัลติเธรดรู้หรือไม่? ใช่. stackoverflow.com/questions/13459447/…
TamusJRoyce

2
@TamusJRoyce หมายเหตุว่าคำสั่งของคุณเป็นจริงเพียงเพื่อIEnumerable<T>เป็นไม่ใช่ทั่วไปไม่ใช้IEnumerable IDisposable
Ian Kemp

9

วิธีที่ฉันทำโดยใช้ประโยชน์จากคุณสมบัติ C # บางอย่างที่ทันสมัย:

ตัวเลือกที่ 1)

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

ตัวเลือก 2)

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

และโดยวิธีการที่ไม่เคยใช้Count == 0หรือCount() == 0เพียงเพื่อตรวจสอบว่าคอลเลกชันที่ว่างเปล่า ใช้ Linq's เสมอ.Any()


2
นับ == 0 ก็โอเค .... อาจเร็วกว่า Any ()? อย่างไรก็ตามคุณถูกต้องในการนับ () == 0 ไม่ดี สำหรับผู้ที่สงสัยว่า Count () จะทำซ้ำผ่านคอลเล็กชั่นทั้งหมดของคุณดังนั้นถ้ามันมีขนาดใหญ่มากก็สามารถเพิ่มค่าใช้จ่ายได้มากมาย!
Anthony Nichols

Count () นับเฉพาะการแจงนับหากไม่สามารถส่งไปที่ ICollection ได้ กล่าวอีกนัยหนึ่งเมื่อคุณเรียกใช้เมธอดนี้ถ้ามีคุณสมบัติ Count บนวัตถุอยู่แล้วมันก็แค่คืนค่านั้นและประสิทธิภาพก็ควรจะเหมือนกัน ลองใช้งานได้ที่นี่: Referencesource.microsoft.com/#System.Core/System/Linq/…
Ronald Rey

หากคุณกำลังทำงานกับ IEnumerable การใช้ Count () เพื่อทดสอบความว่างเปล่าเป็นความคิดที่ไม่ดีนับตั้งแต่การใช้ Linq จะย้ำไปทั่วทั้งคอลเลกชันในขณะที่ Any จะย้ายตัววนซ้ำเพียงครั้งเดียว โปรดทราบว่าคุณไม่สามารถใช้คุณสมบัตินับในกรณีนี้เนื่องจากไม่ได้เป็นส่วนหนึ่งของอินเทอร์เฟซ IEnumerable นั่นเป็นเหตุผลที่เป็นความคิดที่ดีกว่าเสมอที่จะใช้ Any () เพื่อทดสอบความว่างเปล่าในทุกสถานการณ์ในความคิดของฉัน
Ronald Rey

ตัวอย่างที่ดีว่าผู้ดำเนินการปฏิเสธไม่สามารถอ่าน!ได้โดยเฉพาะในตัวเลือกที่สอง;)
Fabio

6

สิ่งนี้อาจช่วยได้

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

เริ่มต้นด้วย C # 6 คุณสามารถใช้การเผยแพร่ว่าง :myList?.Any() == true

หากคุณยังพบปัญหานี้มากเกินไปหรือต้องการวิธีการขยายที่ดีฉันขอแนะนำคำตอบของ Matt Greer และ Marc Gravell แต่มีฟังก์ชั่นเพิ่มเติมเล็กน้อยเพื่อความสมบูรณ์

คำตอบของพวกเขาให้ฟังก์ชั่นพื้นฐานที่เหมือนกัน แต่ต่างจากมุมมองอื่น คำตอบของ Matt ใช้string.IsNullOrEmpty-mentality ในขณะที่คำตอบของ Marc ใช้ Linq.Any()ถนนเพื่อทำงานให้เสร็จ

ฉันมีแนวโน้มที่จะใช้.Any()ถนน แต่ต้องการเพิ่มฟังก์ชั่นการตรวจสอบสภาพจากโอเวอร์โหลดอื่น ๆของวิธีการ:

    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);
    }

ดังนั้นคุณยังสามารถทำสิ่งต่าง ๆ เช่น: myList.AnyNotNull(item=>item.AnswerToLife == 42);ตามปกติด้วย.Any()ด้วยการตรวจสอบ null แต่เพิ่ม

โปรดทราบว่าด้วยวิธี C # 6: myList?.Any()ส่งคืน a bool?มากกว่าboolซึ่งเป็นผลกระทบที่แท้จริงของการเผยแพร่ null


1
มีปัญหากับคอลเลกชันหรือไม่ใด ๆ () คือนั่นไม่ใช่สกรรมกริยา เมื่อ null คอลเลกชัน? .Any () == true เป็น false แต่คอลเล็กชัน? .Any () == false ก็เป็นเท็จด้วยเช่นกัน ยิ่งกว่านั้นคอลเล็กชัน! .Any () == false ก็เป็นเท็จด้วย ...
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

นี่คือรหัสจากคำตอบของ Marc Gravellพร้อมกับตัวอย่างการใช้งาน

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.");
        }
    }
}

ในขณะที่เขากล่าวว่าลำดับไม่สามารถทำซ้ำได้ดังนั้นบางครั้งรหัสอาจทำให้เกิดปัญหาเพราะIsAny()เริ่มก้าวผ่านลำดับ ฉันสงสัยว่าความหมายของคำตอบของโรเบิร์ตฮาร์วีย์คือคุณมักไม่จำเป็นต้องตรวจสอบnull และว่างเปล่า บ่อยครั้งที่คุณสามารถตรวจสอบค่าว่างแล้วใช้foreachบ่อยครั้งที่คุณก็สามารถตรวจสอบโมฆะและการใช้งานแล้ว

เพื่อหลีกเลี่ยงการเริ่มต้นลำดับสองครั้งและใช้ประโยชน์จากforeachฉันเพิ่งเขียนโค้ดบางอย่างเช่นนี้:

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.");
        }
    }
}

ฉันเดาว่าวิธีการขยายช่วยให้คุณพิมพ์ไม่กี่บรรทัด แต่รหัสนี้ดูชัดเจนสำหรับฉัน ฉันสงสัยว่านักพัฒนาบางคนจะไม่ทราบทันทีว่าIsAny(items)จริง ๆ แล้วจะเริ่มก้าวผ่านลำดับ (แน่นอนว่าถ้าคุณใช้ลำดับมากคุณจะเรียนรู้ที่จะทำตามขั้นตอนเหล่านั้นอย่างรวดเร็ว)


ถ้าคุณโทรหา IsAny ด้วยค่า null มันจะทำให้เกิดข้อยกเว้น
Ace Trajkov

3
คุณลองมันแล้ว @Ace? ดูเหมือนว่าจะทำให้เกิดข้อยกเว้น แต่สามารถเรียกวิธีการขยายในอินสแตนซ์ที่เป็นโมฆะได้
Don Kirkby

2

ฉันใช้ Bool IsCollectionNullOrEmpty = !(Collection?.Any()??false);ฉันใช้หวังว่านี่จะช่วยได้

ทำให้พังถล่ม:

Collection?.Any()จะกลับมาnullถ้าคอลเล็กชันเป็นโมฆะและfalseถ้าคอลเลกชันว่างเปล่า

Collection?.Any()??falseจะให้เราfalseถ้าคอลเลกชันว่างเปล่าและfalseถ้าคอลเลกชันคือnullถ้าคอลเลกชัน

IsEmptyOrNullเสริมการที่จะให้เรา


2

Anwser ของ Jon Skeet ( https://stackoverflow.com/a/28904021/8207463 ) มีวิธีการที่ดีโดยใช้วิธีการขยาย - ใด ๆ () สำหรับ NULL และว่างเปล่า แต่เขาจะตรวจสอบคำถามของเจ้าของในกรณีที่ไม่ใช่ค่า NULL ดังนั้นเปลี่ยนแนวทางของ Jon อย่างระมัดระวังเพื่อตรวจสอบ AS NULL เป็น:

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

ห้ามใช้ (จะไม่ตรวจสอบเป็น NULL):

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

นอกจากนี้คุณยังสามารถตรวจสอบ AS NOT NULL (ไม่ทดสอบเช่นเดียวกับตัวอย่าง แต่ไม่มีข้อผิดพลาดของคอมไพเลอร์) ทำบางอย่างเช่นการใช้เพรดิเคต:

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

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

สำหรับเวอร์ชั่น NET ที่คุณสามารถใช้งานได้โปรดตรวจสอบ:

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


1

ฉันมีปัญหาเดียวกันและฉันแก้มันเหมือน:

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

"c => c! = null" จะไม่สนใจเอนทิตี null ทั้งหมด


1

ฉันสร้างคำตอบนี้โดย @Matt Greer

เขาตอบคำถามของ OP อย่างสมบูรณ์แบบ

ฉันต้องการสิ่งนี้ในขณะที่รักษาความสามารถดั้งเดิมของ Any ขณะเดียวกันก็ตรวจสอบ null ฉันโพสต์สิ่งนี้ในกรณีที่คนอื่นต้องการอะไรที่คล้ายกัน

โดยเฉพาะฉันต้องการที่จะยังคงสามารถผ่านในภาคแสดง

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;
    }
}

การตั้งชื่อของวิธีการขยายอาจจะดีกว่า


0

ทางออกที่ดีที่สุดอื่น ๆ ดังต่อไปนี้เพื่อตรวจสอบว่างเปล่าหรือไม่?

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

1
ที่จะไม่ทำงานถ้าlistEnumerableเป็นโมฆะซึ่งเป็นคำถามที่อยู่ในมือ
Timotei

0

ฉันใช้อันนี้:

    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

เนื่องจากทรัพยากรบางอย่างหมดไปหลังจากอ่านครั้งเดียวฉันจึงคิดว่าทำไมไม่รวมเช็คและอ่านเข้าด้วยกันแทนที่จะเป็นเช็คแบบดั้งเดิมแยกต่างหากจากนั้นอ่าน

ก่อนอื่นเรามีหนึ่งส่วนขยายอินไลน์ check-for-null ที่ง่ายกว่า:

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();

จากนั้นเรามีส่วนเกี่ยวข้องน้อยมาก (อย่างน้อยก็อย่างที่ฉันเขียนไว้) ส่วนขยายอินไลน์ตรวจสอบค่าว่างและว่าง:

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();

แน่นอนว่าคุณยังสามารถโทรหาทั้งคู่ได้โดยไม่ต้องต่อสายโซ่ นอกจากนี้ฉันรวม paramName เพื่อให้ผู้โทรอาจมีชื่ออื่นสำหรับข้อผิดพลาดหากไม่มีการตรวจสอบ "แหล่งที่มา" เช่น "nameof (target)"


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

วิธีการขยายของฉันเองเพื่อตรวจสอบไม่เป็นโมฆะและใด ๆ


0

หากไม่มีผู้ช่วยที่กำหนดเองฉันขอแนะนำอย่างใดอย่างหนึ่ง?.Any() ?? falseหรือ?.Any() == trueที่ค่อนข้างกระชับและต้องระบุลำดับเพียงครั้งเดียว


เมื่อฉันต้องการรักษาคอลเลกชันที่ขาดหายไปเช่นเดียวกับที่ว่างเปล่าฉันใช้วิธีการต่อไปนี้:

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

ฟังก์ชั่นนี้สามารถใช้ร่วมกับวิธีการของ LINQ ได้ทั้งหมดและforeachไม่ใช่เพียงแค่.Any()นี้ซึ่งเป็นเหตุผลว่าทำไมฉันถึงชอบมากกว่าฟังก์ชั่นตัวช่วยพิเศษที่ผู้คนกำลังเสนอให้ที่นี่


0

ฉันใช้

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

ผลลัพธ์จะเป็นโมฆะหากไม่มีการจับคู่มิฉะนั้นส่งคืนวัตถุใดวัตถุหนึ่ง

หากคุณต้องการรายการฉันเชื่อว่าการออกจาก First () หรือโทร ToList () จะให้รายชื่อหรือ null



-1

เพียงแค่เพิ่มและดูมายากลเกิดขึ้นเมื่อคุณพยายามเข้าถึงวิธีการที่ใช้ได้ในusing System.Linq IEnumerableการเพิ่มสิ่งนี้จะทำให้คุณสามารถเข้าถึงวิธีการตั้งชื่อCount()แบบง่าย ๆ ได้ เพียงอย่าลืมตรวจสอบnull valueก่อนโทรcount():)


-1

ฉันใช้ง่ายถ้าจะตรวจสอบมัน

ตรวจสอบทางออกของฉัน

foreach (Pet pet in v.Pets)
{
    if (pet == null)
    {
        Console.WriteLine(" No pet");// enumerator is empty
        break;
    }
    Console.WriteLine("  {0}", pet.Name);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.