空字符串的合并函数 Coalesce?

214

我发现自己越来越经常使用条件运算符来检查字符串是否为空(例如""或null)。

一个当前的例子:

s.SiteNumber.IsNullOrEmpty() ? "No Number" : s.SiteNumber;

这只是一个扩展方法,它等同于:

string.IsNullOrEmpty(s.SiteNumber) ? "No Number" : s.SiteNumber;

由于它是空的而不是null,??不能起作用。 string.IsNullOrEmpty()版本的??将是完美的解决方案。我认为必须有更简洁的方法来做到这一点(希望如此!),但我一直找不到。

是否有人知道更好的方法来做到这一点,即使只在.Net 4.0中也可以?


13
为了吊起你的胃口,你可以在F#中轻松定义自定义的即时二元(和一元,同样如此)运算符。这里是 let (|?) x y = if String.IsNullOrEmpty(x) then y else x,并像这样使用它 s.SiteNumber |? "No Number" - Stephen Swensen
12个回答

206

C# 已经允许我们用 ??null 替换值。因此,我们只需要一个将空字符串转换为 null 的扩展方法,然后像这样使用它:

s.SiteNumber.NullIfEmpty() ?? "No Number";

扩展类:

public static class StringExtensions
{
    public static string NullIfEmpty(this string s)
    {
        return string.IsNullOrEmpty(s) ? null : s;
    }
    public static string NullIfWhiteSpace(this string s)
    {
        return string.IsNullOrWhiteSpace(s) ? null : s;
    }
}

这种方法似乎更具有自我记录性,因此被点赞了... - Allen

86

没有内置的方法来实现这个。但是,你可以将扩展方法返回一个字符串或 null,这样就可以使用合并运算符了。然而,这会很奇怪,我个人更喜欢你当前的方法。

既然你已经在使用扩展方法,为什么不创建一个返回值或默认值的扩展方法呢:

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");

8
我认为你说得对,这是目前可读性最好的最清晰的解决方案。不过,我希望C# 5里面有一个类似于 ??? 的操作符,谁知道呢。 - Nick Craver
2
“???” 运算符会做什么?除了 null 值,还会采用默认值吗?听起来最多是极其复杂的。 - bevacqua
1
使用lambda表达式可能吗? 例如:假设“item”可为空,那么...item ?? x => x.ToString():null; - Isaac Llopis
@IsaacLlopis 最终看起来比 OP 的原始片段更凌乱。 - maraaaaaaaa
我更喜欢像 ReplaceNullOrEmptyBy() 这样的名称,因为它不是转换。但这个概念本身有一个弱点:如果要替换的 string? 变量不是由字符串字面量替换,而是由另一个或多个链接的 string? 变量最终替换为字符串字面量,则它将无法工作。链接调用将再次创建可空性警告。 - Tobias Knauss

59

我知道这是一个老问题——但我正在寻找答案,以上所有答案都没有像我最终使用的那样适合我的需求:

private static string Coalesce(params string[] strings)
{
    return strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
}

用法:

string result = Coalesce(s.SiteNumber, s.AltSiteNumber, "No Number");

编辑: 一个更加简洁的编写此函数的方式是:

static string Coalesce(params string[] strings) => strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));

1
我喜欢这个,但不得不修复编译器错误并使其更加紧凑。 - gregmac
为什么你把这个叫做Coalesce,它并没有将值合并在一起,而只是选择了不为空的一个?这个名字有点让人困惑啊。 - Jimmyt1988
15
Coalesce是许多数据库用来执行相同操作(查找第一个非空值)的术语。将字符串连接在一起称为串联(concatenation)。 - Cameron Forward
如果您正在使用 System.Linq,那么最佳答案是: - JoePC
很优雅,干得好。 - Mariano Luis Villa
字符串默认为null,因此此函数要求返回类型为string?FirstOrDefault(..) ?? "" - undefined

20

我有几个实用的扩展插件,我喜欢使用:

public static string OrDefault(this string str, string @default = default(string))
{
    return string.IsNullOrEmpty(str) ? @default : str;
}

public static object OrDefault(this string str, object @default)
{
    return string.IsNullOrEmpty(str) ? @default : str;
}

如果您希望将其应用于任何类型,也可以使用此选项:

public static T OrDefault<T>(this T obj, T @default)
{
    return EqualityComparer<T>.Default.Equals(obj, default(T)) ? @default : obj;
}    

编辑:受到sfsr答案的启发,我将从现在开始将这个变量添加到我的工具箱中:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}

2
我肯定会使用“Coalesce”这个术语,因为它更接近空合并运算符(??)的意图,尽管我将其改为“CoalesceTo”。 - llaughlin
@default 参数前缀的 @ 是什么意思?我以前从未见过。 - Drew Chapin
3
@druciferre - 这只是允许您在 C# 中将 default 用作变量名,尽管它是一个保留关键字。 - Justin Morgan
为什么你把这个叫做Coalesce,它并没有将值合并在一起,而只是选择了不为空的一个?这个名字有点让人困惑啊。 - Jimmyt1988
1
@Jimmyt1988 - 因为它近似于标准的 T-SQL COALESCE 函数。 - Justin Morgan
2
@Jimmyt1988 - 这也是因为它特别选择了任意长度列表中的第一个非空函数。这是一个微妙的细节,但T-SQL函数的工作方式相同。对于任何知道该函数的人来说,名称使其直观易懂,无论是否有文档。 - Justin Morgan

9

我通常使用NullIfEmpty扩展方法,如果字符串为空,则始终返回null,从而允许像往常一样使用??(Null Coalescing Operator)。

public static string NullIfEmpty(this string s)
{
    return string.IsNullOrEmpty(s) ? null : s;
}

这将允许??正常使用,并使链接易于阅读。
string string1 = string2.NullIfEmpty() ?? string3.NullIfEmpty() ?? string4;

7

空值合并运算符的优点之一是短路操作。当第一个部分不为空时,第二部分不会被计算。当回退需要进行耗费大量操作时,这非常有用。

最终结果如下:

public static string Coalesce(this string s, Func<string> func)
{
    return String.IsNullOrEmpty(s) ? func() : s;
}

使用方法:

string navigationTitle = model?.NavigationTitle.
    Coalesce(() => RemoteTitleLookup(model?.ID)). // Expensive!
    Coalesce(() => model?.DisplayName);

6

也许比之前提出的更快的扩展方法:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}

12
为什么不使用IsNullOrWhitespace,而不是使用trim和length呢? - weston
1
公共静态字符串 Coalesce(this string @this,string @default =“”) { 返回(@this == null || String.IsNullOrWhiteSpace(@this))?@default:@this; } - paul-2011

4
如何添加一个字符串扩展方法 ValueOrDefault()?
public static string ValueOrDefault(this string s, string sDefault)
{
    if (string.IsNullOrEmpty(s))
        return sDefault;
    return s;
}

如果字符串为空,则返回 null:
public static string Value(this string s)
{
    if (string.IsNullOrEmpty(s))
        return null;
    return s;
}

虽然我没有尝试过这些解决方案。


2
我喜欢那里的选项#1,尽管我会将其称为更语义化的名称,例如Or(),这样我就可以编写“string s = s.SiteNumber.Or(“Default”);”。 - Jerod Venema
2
如果...OrDefault()的行为与框架中其他...OrDefault()方法不同,那么给它命名会很困惑。不管你喜欢与否,微软已经为这个命名赋予了特定的含义,在自定义方法中偏离这种行为只会让你的API用户感到不必要的困惑。 - mattmc3

4

我正在使用自己的字符串Coalesce扩展方法。由于这里的人正在使用LINQ,并且在耗时操作上浪费了大量资源(我在紧密循环中使用它),因此我将分享我的方法:

public static class StringCoalesceExtension
{
    public static string Coalesce(this string s1, string s2)
    {
        return string.IsNullOrWhiteSpace(s1) ? s2 : s1;
    }
}

我认为这很简单,甚至不需要处理null字符串值。使用方法如下:

string s1 = null;
string s2 = "";
string s3 = "loudenvier";
string s = s1.Coalesce(s2.Coalesce(s3));
Assert.AreEqual("loudenvier", s);

我经常使用它。这是那些“必不可少”的实用函数之一,第一次使用后就无法离开它:-)


我认为你不明白他们为什么使用LINQ,而且由于参数在调用之前被求值,你的s2.Coalesce(s3)即使不需要也会运行。最好使用NullIfEmpty()扩展和?? - NetMage
@NetMage 我可以保证,LINQ 版本的性能比我提供的版本要差很多。如果您愿意,可以创建一个简单的基准测试来测试这一点。我建议使用 https://github.com/dotnet/BenchmarkDotNet 来避免编写基准测试代码时的常见陷阱。 - Loudenvier

0
sqlCom.Parameters.Add(new SqlParameter("@qavCode", SqlDbType.Char, 11)).Value = (object)(string.IsNullOrEmpty(rf.Request.QavCode) ? null : rf.Request.QavCode) ?? DBNull.Value;

不知道,也许我回答这个问题太晚了,但是在我的例子中,我混合使用了常规三元运算符 ?: 和空值合并运算符 ??,以便它可以与空字符串一起使用。希望能有所帮助 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接