我知道通常空列表比NULL更可取。但是我将返回NULL,主要有两个原因
- 我必须显式检查并处理null值,以避免bug和攻击。
??
之后很容易执行操作以获得返回值。
对于字符串,我们有IsNullOrEmpty。有什么从C#本身做了列表或IEnumerable的同样的事情?
我知道通常空列表比NULL更可取。但是我将返回NULL,主要有两个原因
??
之后很容易执行操作以获得返回值。对于字符串,我们有IsNullOrEmpty。有什么从C#本身做了列表或IEnumerable的同样的事情?
Answers:
框架中没有任何东西,但这是一个非常简单的扩展方法。
/// <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的整个问题),但是在某些情况下将其与空枚举合并可以更方便一些,只使用结果即可。
ICollection
,因为anIEnumerable<Animal>
可能类似于的实例List<Cat>
,该实例没有实现ICollection<Animal>
但确实实现了非泛型ICollection
?
后期更新:从C#6.0开始,可以使用null传播运算符来表示简洁,如下所示:
if ( list?.Count > 0 ) // For List<T>
if ( array?.Length > 0 ) // For Array<T>
或者,作为更清洁,更通用的替代品IEnumerable<T>
:
if ( enumerable?.Any() ?? false )
注1:IsNotNullOrEmpty
与OP问题(引用)相比,所有较高的变体实际上都反映出来:
由于运算符优先级,因此
IsNullOrEmpty
等效项看起来不太吸引人:
if (!(list?.Count > 0))
注意2: ?? false
是必要的,因为以下原因(此帖子的摘要/引用):
?.
null
如果子成员为,运算符将返回null
。但是,如果我们尝试让非Nullable
成员(如Any()
方法)返回bool
[...],则编译器将“包装”返回值Nullable<>
。例如,Object?.Any()
将给我们bool?
(是Nullable<bool>
),而不是bool
。[...]由于无法将其隐式转换bool
为该表达式,因此无法在if
注意3:作为奖励,该语句也是“线程安全的”(引用此问题的答案):
在多线程上下文中,如果[枚举]可从另一个线程访问(或者因为它是可访问的字段,或者因为它在暴露给另一个线程的lambda中关闭了),则每次计算时该值可能都不同[ ieprior null -检查]
(myArray?.Length ?? 0) < 2
没有内置任何内容。
但是,这是一个简单的扩展方法:
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
if(enumerable == null)
return true;
return !enumerable.Any();
}
var nullOrEmpty = list == null || !list.Any();
如果在不为空的情况下需要能够检索所有元素,则此处的某些答案将不起作用,因为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。
IEnumerable
则更是如此,因为查询将丢失。
就像其他人都说的那样,框架没有任何内容,但是如果您使用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;
}
...
我修改了Matthew Vines的建议,以避免出现“可能的IEnumerable多重枚举”的问题。(另请参阅乔恩·汉纳的评论)
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());
}
var nullOrEmpty = !( list?.Count > 0 );