一句话总结:便利到“语义开始变得模糊”的地方就该停下。
string.IsNullOrEmpty 之所以成立,是因为它的含义几乎没有争议:
null:没有值"":有值但长度为 0这对字符串是一个清晰、通用、可预期的定义。
但当你把问题扩展到集合、甚至“混合类型集合”时,麻烦就来了:
0、false、Guid.Empty 算不算“空”?" "(只含空白)算不算“空”?这些都不是技术问题,而是业务语义问题。框架如果给一个“默认实现”,必然会把某种主观选择变成“看起来像标准答案”的 API,从而制造误用。
一个可以进入 .NET Base Class Library 的 API,必须满足:
string.IsNullOrEmpty 满足。
而 IsNullOrEmpty(IEnumerable<T>) 这种泛化版本,很容易变成“写起来爽、读起来危险”的工具。
例如:针对 IEnumerable<string?> 你就可以在团队里明确约定:
null 或者没有元素 ⇒ 认为“空”null/empty/whitespace ⇒ 认为“包含空值”示例(自写):
public static class StringCollectionExtensions
{
public static bool IsNullOrEmptyOrAnyBlank(this IEnumerable<string?>? source)
{
if (source is null) return true;
using var e = source.GetEnumerator();
if (!e.MoveNext()) return true; // empty
foreach (var s in source)
{
if (string.IsNullOrWhiteSpace(s)) return true;
}
return false;
}
}
当你的集合可能是 object? 或者需要按场景切换规则时,最稳妥的做法是把“空”交给调用方定义:
public static class CollectionPolicyExtensions
{
public static bool IsNullOrEmpty<T>(this IEnumerable<T>? source, Func<T, bool> isEmpty)
{
if (source is null) return true;
using var e = source.GetEnumerator();
if (!e.MoveNext()) return true;
foreach (var item in source)
{
if (isEmpty(item)) return true;
}
return false;
}
}
使用:
var values = new object?[] { "A", "", 12, null };
bool bad = values.IsNullOrEmpty(x =>
x is null ||
(x is string s && string.IsNullOrWhiteSpace(s))
);
框架停止在 string.IsNullOrEmpty 不是疏漏,而是有意为之: