我知道通常空列表比NULL更受青睐。但我要返回NULL,主要是出于以下两个原因:
- 我必须显式地检查和处理null值,以避免bug和攻击。
- 之后执行
??
操作很容易得到一个返回值。
对于字符串,我们有IsNullOrEmpty。是否有任何来自C#本身可以做到与List或IEnumerable相同的功能?
我知道通常空列表比NULL更受青睐。但我要返回NULL,主要是出于以下两个原因:
??
操作很容易得到一个返回值。对于字符串,我们有IsNullOrEmpty。是否有任何来自C#本身可以做到与List或IEnumerable相同的功能?
这个框架中没有任何内置的内容,但它是一个相当简单直接的扩展方法。
点击此处查看更多信息。
/// <summary>
/// Determines whether the collection is null or contains no elements.
/// </summary>
/// <typeparam name="T">The IEnumerable type.</typeparam>
/// <param name="enumerable">The enumerable, which may be null or empty.</param>
/// <returns>
/// <c>true</c> if the IEnumerable is null or empty; otherwise, <c>false</c>.
/// </returns>
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
if (enumerable == null)
{
return true;
}
/* If this is a list, use the Count property for efficiency.
* The Count property is O(1) while IEnumerable.Count() is O(N). */
var collection = enumerable as ICollection<T>;
if (collection != null)
{
return collection.Count < 1;
}
return !enumerable.Any();
}
为了提高性能,Daniel Vaughan采取了将数据类型转换为ICollection的额外步骤(在可能的情况下)。这是我没有想到的。
enumerable.Any()
可能会丢失不可重复枚举的元素。您可以将其包装在某些东西中,以跟踪第一个元素(当然要处理null或否定OP的整个问题),但在某些情况下,与空枚举合并并仅使用结果可能更方便。 - Jon HannaIReadOnlyCollection
(不继承于ICollection)的短路处理。 - Lee Grissom最新更新:自C# 6.0以来,可以使用空值传递运算符来表达类似于以下简洁的代码:
if ( list?.Count > 0 ) // For List<T>
if ( array?.Length > 0 ) // For Array<T>
或者,作为一个更干净、更通用的替代方案,使用 IEnumerable<T>
:
if ( enumerable?.Any() ?? false )
注意1:所有大写的变量实际上反映的是IsNotNullOrEmpty
,与OP问题不同(引用)。
注意2:由于运算符优先级,
IsNullOrEmpty
的等效物看起来不太吸引人:
if (!(list?.Count > 0))
?? false
是必要的,因为以下原因(摘自这篇文章):
注意3:作为奖励,该语句也是“线程安全”的(引自这个问题):
?.
运算符如果一个子成员是null
,将返回null
。 但是[...] 如果我们尝试获取非Nullable
成员,比如返回bool
的Any()
方法[...]编译器将会 "wrap" 一个返回值在Nullable<>
中。例如,Object?.Any()
将给我们bool?
(这是Nullable<bool>
),而不是bool
。 [...] 由于它无法被隐式转换为bool
,因此此表达式不能用在if
语句中。
在多线程上下文中,如果[enumerable]可以从另一个线程访问(因为它是可访问的字段,或者因为它是 封闭在对另一个线程可见的lambda中),那么每次计算 [i.e. 在之前的null检查之前] 值可能不同。
(myArray?.Length ?? 0) < 2
- intrepidis没有内置的功能。
但是有一个简单的扩展方法:
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
if(enumerable == null)
return true;
return !enumerable.Any();
}
var nullOrEmpty = list == null || !list.Any();
将之前的答案整合成一个简单的C# 6.0+扩展方法:
public static bool IsNullOrEmpty<T>(this IEnumerable<T> me) => !me?.Any() ?? true;
正如其他人所说,框架本身并没有包含这个功能,但如果你正在使用Castle的话,Castle.Core.Internal库中有它。
using Castle.Core.Internal;
namespace PhoneNumbers
{
public class PhoneNumberService : IPhoneNumberService
{
public void ConsolidateNumbers(Account accountRequest)
{
if (accountRequest.Addresses.IsNullOrEmpty()) // Addresses is List<T>
{
return;
}
...
Any()
将“忘记”一个元素。bool didSomething = false;
foreach(var element in someEnumeration ?? Enumerable.Empty<MyType>())
{
//some sensible thing to do on element...
didSomething = true;
}
if(!didSomething)
{
//handle the fact that it was null or empty (without caring which).
}
(someEnumeration ?? Enumerable.Empty<MyType>()).ToList()
等语句。foreach(var item in someQueryable)
将生成自己的枚举器,然后执行someQueryable.Any()
将是一个单独的查询,并不会影响当前的foreach枚举器AFAIK。 - AaronLSIEnumerable
,那么情况就更糟了,因为查询将会丢失。 - Jon Hannavar nullOrEmpty = !( list?.Count > 0 );
我修改了Matthew Vines的建议,以避免“可能多次枚举IEnumerable”的问题。(另请参阅Jon Hanna的评论)
public static bool IsNullOrEmpty(this IEnumerable items)
=> items == null
|| (items as ICollection)?.Count == 0
|| !items.GetEnumerator().MoveNext();
...以及单元测试:
[Test]
public void TestEnumerableEx()
{
List<int> list = null;
Assert.IsTrue(list.IsNullOrEmpty());
list = new List<int>();
Assert.IsTrue(list.IsNullOrEmpty());
list.AddRange(new []{1, 2, 3});
Assert.IsFalse(list.IsNullOrEmpty());
var enumerator = list.GetEnumerator();
for(var i = 1; i <= list.Count; i++)
{
Assert.IsFalse(list.IsNullOrEmpty());
Assert.IsTrue(enumerator.MoveNext());
Assert.AreEqual(i, enumerator.Current);
}
Assert.IsFalse(list.IsNullOrEmpty());
Assert.IsFalse(enumerator.MoveNext());
}
带有可空支持的一行代码:
public static bool IsNullOrEmpty<T>(this IEnumerable<T>? enumerable) =>
enumerable == null || !enumerable.Any();