Имеет ли С# IsNullOrEmpty для List/IEnumerable?
Я знаю, что обычно пустой список более предпочтителен, чем NULL. Но я собираюсь вернуть NULL, в основном по двум причинам.
- Я должен явно проверять и обрабатывать нулевые значения, избегая ошибок и атак.
- Легко выполнить операцию
??
после получения возвращаемого значения.
Для строк у нас есть IsNullOrEmpty. Есть ли что-нибудь от самого С#, которое делает то же самое для List или IEnumerable?
Ответы
Ответ 1
ничего не запечено в фреймворке, но это довольно простой метод расширения.
Смотрите здесь
/// <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();
}
Даниэль Воган делает дополнительный шаг приведения к ICollection (где это возможно) по соображениям производительности. Что-то, чего я бы не подумал сделать.
Ответ 2
Позднее обновление: начиная с С# 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 (цитата):
Из-за приоритета оператора IsNullOrEmpty
эквиваленты выглядят менее привлекательными:
if (!(list?.Count > 0))
Примечание 2: ?? false
необходимо по следующей причине (резюме/цитата из этого сообщения):
Оператор ?.
вернет null
, если дочерним элементом является null
. Но [...] если мы попытаемся получить член non- Nullable
, например, Any()
метод, который возвращает bool
[...] компилятор "обернуть" возвращаемое значение в Nullable<>
. Например, Object?.Any()
будет дайте нам bool?
(то есть Nullable<bool>
), а не bool
. [...] Поскольку оно не может быть неявно приведено к bool
, это выражение нельзя использовать в if
Примечание 3: в качестве бонуса выражение также "поточно-ориентировано" (цитата из ответа на этот вопрос):
В многопоточном контексте, если [enumerable] доступен из другого поток (или потому, что это поле, которое доступно или потому что это закрыт в лямбду, которая подвергается воздействию другого потока), то значение может отличаться каждый раз, когда оно вычисляется [т.е .prior null-check]
Ответ 3
Нет ничего встроенного.
Это простой метод расширения:
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
if(enumerable == null)
return true;
return !enumerable.Any();
}
Ответ 4
var nullOrEmpty = list == null || !list.Any();
Ответ 5
Объединение предыдущих ответов в простой метод расширения для С# 6.0 +:
public static bool IsNullOrEmpty<T>(this IEnumerable<T> me) => !me?.Any() ?? true;
Ответ 6
Если вам нужно получить все элементы в случае, если он не будет пустым, то некоторые из ответов здесь не будут работать, потому что вызов Any()
на неперематываемый перечислимый будет "забыть" элемент.
Вы можете использовать другой подход и превратить nulls в пустые контейнеры:
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()
и т.д.
Ответ 7
Как и все остальные, в инфраструктуру не встроено ничего, но если вы используете 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;
}
...
Ответ 8
Я модифицировал предложение от Мэтью Вина, чтобы избежать "возможного множественного перечисления IEnumerable" - проблемы.
(см. также комментарий от Джона Ханны)
public static bool IsNullOrEmpty(this IEnumerable items)
=> items == null
|| (items as ICollection)?.Count == 0
|| !items.GetEnumerator().MoveNext();
... и unit test:
[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());
}
Ответ 9
для меня лучший метод isNullOrEmpty выглядит так
public static bool IsNullOrEmpty<T>(this IEnumerable<T> enumerable)
{
return !enumerable?.Any() ?? true;
}
Ответ 10
var nullOrEmpty = !( list?.Count > 0 );