.NET 为什么只有 string.IsNullOrEmpty?——便利与语义边界

作者:微信公众号:【架构师老卢】
2-1 10:14
34

一句话总结:便利到“语义开始变得模糊”的地方就该停下

string.IsNullOrEmpty 之所以成立,是因为它的含义几乎没有争议:

  • null:没有值
  • "":有值但长度为 0

这对字符串是一个清晰、通用、可预期的定义。

但当你把问题扩展到集合、甚至“混合类型集合”时,麻烦就来了:

  • 空集合算不算“空”?
  • 集合里只要有一个元素为空就算“空”?还是全部为空才算?
  • 0falseGuid.Empty 算不算“空”?
  • " "(只含空白)算不算“空”?

这些都不是技术问题,而是业务语义问题。框架如果给一个“默认实现”,必然会把某种主观选择变成“看起来像标准答案”的 API,从而制造误用。

为什么 BCL 不做泛化:API 必须有唯一的普适含义

一个可以进入 .NET Base Class Library 的 API,必须满足:

  • 行为明确
  • 语义普适
  • 大多数场景下不会误导

string.IsNullOrEmpty 满足。

IsNullOrEmpty(IEnumerable<T>) 这种泛化版本,很容易变成“写起来爽、读起来危险”的工具。

正确做法 1:为强类型集合定义清晰规则(推荐)

例如:针对 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;
    }
}

正确做法 2:把“空”的判断做成策略(Policy)

当你的集合可能是 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 不是疏漏,而是有意为之:

  • 便利应该存在,但不能用“看似通用”的 API 掩盖语义差异
  • 一旦进入“该怎么定义空?”这种问题,答案就属于你的领域/团队
相关留言评论
昵称:
邮箱:
阅读排行