??合并空字符串?
Posted
技术标签:
【中文标题】??合并空字符串?【英文标题】:?? Coalesce for empty string? 【发布时间】:2011-01-26 01:57:25 【问题描述】:我发现自己做的越来越多的事情是检查字符串是否为空(如""
或 null)和条件运算符。
当前示例:
s.SiteNumber.IsNullOrEmpty() ? "No Number" : s.SiteNumber;
这只是一个扩展方法,相当于:
string.IsNullOrEmpty(s.SiteNumber) ? "No Number" : s.SiteNumber;
由于它是空的且不为空,??
不会起作用。 string.IsNullOrEmpty()
版本的??
将是完美的解决方案。我认为必须有一种更清洁的方式来做到这一点(我希望!),但我一直不知所措。
有没有人知道更好的方法来做到这一点,即使它只在 .Net 4.0 中?
【问题讨论】:
只是为了吸引您一点,您可以在 F# 中轻松定义自定义的临时二元(和一元)运算符。在这里let (|?) x y = if String.IsNullOrEmpty(x) then y else x
并像s.SiteNumber |? "No Number"
一样使用它。
【参考方案1】:
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;
【讨论】:
这种方法似乎更具自我记录性,因此受到支持......【参考方案2】:没有内置的方法可以做到这一点。但是,您可以使您的扩展方法返回一个字符串或 null,这将允许合并运算符工作。然而,这会很奇怪,我个人更喜欢你目前的方法。
既然你已经在使用扩展方法,为什么不直接做一个返回值或默认值的方法:
string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
【讨论】:
我认为你是对的,这是目前可用的最干净的解决方案,仍然可读。我喜欢 C# 5 中的???
运算符之类的东西,谁知道呢。
那会是什么???运营商呢?除空值外还采用默认值?充其量听起来非常复杂
可以使用 lambda 表达式吗?例如:假设“item”可以为空,那么...item ?? x=> x.ToString() : null;
@IsaacLlopis 最终看起来比 OP 的原始 sn-p 更混乱【参考方案3】:
我知道这是一个老问题 - 但我一直在寻找答案,但以上都不符合我的需要以及我最终使用的:
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));
【讨论】:
我喜欢,但必须修复编译器错误并使其更紧凑。 当它没有将值组合在一起时,为什么你称之为 Coalesce,而只是选择不为空的那个?这是一个令人困惑的名字,伙计。 因为 Coalesce 是许多数据库用来执行相同操作的术语(查找第一个非空值)。将字符串连接在一起就是串联。 最佳答案using System.Linq
这很优雅,干得好。【参考方案4】:
我有几个我喜欢使用的实用程序扩展:
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;
编辑:受 sfsr 回答的启发,我将从现在开始将此变体添加到我的工具箱中:
public static string Coalesce(this string str, params string[] strings)
return (new[] str)
.Concat(strings)
.FirstOrDefault(s => !string.IsNullOrEmpty(s));
【讨论】:
我肯定在使用“Coalesce”术语,因为它更类似于空合并运算符 (??) 的意图,尽管我将其更改为“CoalesceTo”。@default
参数上的@
前缀有什么作用?我以前从未见过。
@druciferre - 这只是允许您使用 default
作为变量名,即使它是 C# 中的保留关键字。
@Jimmyt1988 - 因为它近似于标准 T-SQL COALESCE 函数。
@Jimmyt1988 - 还因为它专门选择了任意长度列表中的 first 非空函数。这是一个微妙的细节,但 T-SQL 函数的工作方式相同。这个名称让任何知道该功能的人都可以直观地了解该功能,无论是否有文档。【参考方案5】:
null-coalescing 运算符的优点之一是它可以短路。当第一部分不为空时,不评估第二部分。当回退需要昂贵的操作时,这可能很有用。
我最终得到:
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】:我只是使用 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 Fallback(this string @this, string @default = "")
return (@this == null || @this.Trim().Length == 0) ? @default : @this;
【讨论】:
为什么不使用IsNullOrWhitespace而不是修剪和延长它。code
public static string Coalesce(this string @this, string @default = "") return (@this == null || String.IsNullOrWhiteSpace(@this)) ? @默认:@this; 【参考方案8】:
字符串扩展方法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;
虽然没有尝试这些解决方案。
【讨论】:
我喜欢那里的选项#1,虽然我会称它为更语义化的东西,比如 Or(),所以我可以写“string s = s.SiteNumber.Or("Default");" 如果调用...OrDefault()
的行为与框架的其他 ...OrDefault()
方法不同,它会让人感到困惑。不管喜欢与否,MS 赋予了特定含义,即在自定义方法中命名和偏离该行为会给 API 的用户带来不必要的混淆。【参考方案9】:
我正在使用我自己的字符串 Coalesce 扩展方法。由于这里使用的是 LINQ,并且绝对浪费资源进行时间密集型操作(我在紧密循环中使用它),我将分享我的:
public static class StringCoalesceExtension
public static string Coalesce(this string s1, string s2)
return string.IsNullOrWhiteSpace(s1) ? s2 : s1;
我认为这很简单,您甚至不需要为空字符串值而烦恼。像这样使用它:
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 我可以向您保证,LINQ 版本的性能远低于我介绍的版本。如果您愿意,您可以创建一个简单的基准来测试它。我建议在编写基准测试代码时使用github.com/dotnet/BenchmarkDotNet 来防止常见的陷阱。【参考方案10】:
我喜欢下面的扩展方法QQQ
的简洁性,虽然当然像运算符一样?会更好。但是我们可以通过允许不只比较两个而是三个字符串选项值来提高这一点,其中一个需要不时处理(见下面的第二个函数)。
#region QQ
[DebuggerStepThrough]
public static string QQQ(this string str, string value2)
return (str != null && str.Length > 0)
? str
: value2;
[DebuggerStepThrough]
public static string QQQ(this string str, string value2, string value3)
return (str != null && str.Length > 0)
? str
: (value2 != null && value2.Length > 0)
? value2
: value3;
// Following is only two QQ, just checks null, but allows more than 1 string unlike ?? can do:
[DebuggerStepThrough]
public static string QQ(this string str, string value2, string value3)
return (str != null)
? str
: (value2 != null)
? value2
: value3;
#endregion
【讨论】:
如果您喜欢短名称,可以将其命名为Or
,我会使用 params
关键字,就像其他答案一样,这样可以避免多个参数的重复定义。
感谢您的想法。在我自己的用法中,我早就用“FirstNotNull”替换了这个名称。在params
上,最好不要在默认情况下或两种情况下这样做,因为params
在您只有默认的一或两个输入时会导致不必要地分配数组。之后才有意义。【参考方案11】:
很容易将一些答案转化为具有泛型的辅助扩展类,以便更广泛地使用:
注意:有关短路方法的说明,请参阅wensveen answer
// classic
public static string Coalesce(this string s, params string[] strings)
=> s.Coalesce(string.IsNullOrEmpty, strings);
// short-circuit compatible, for expensive string getting
public static string Coalesce(this string s, params Func<string>[] getters)
=> s.Coalesce(string.IsNullOrEmpty, getters);
// generic
public static T Coalesce<T>(this T value, Func<T, bool> isEmpty, params T[] values) where T : class
=> isEmpty(value) ? values.FirstOrDefault(val => !isEmpty(val)) : value;
// generic, short-circuit compatible
public static T Coalesce<T>(this T value, Func<T, bool> isEmpty, params Func<T>[] getters) where T : class
if (isEmpty(value))
return getters
.Select(getter => new Lazy<T>(getter))
.FirstOrDefault(val => !isEmpty(val.Value))
?.Value;
return value;
示例用法:
string result = s.SiteNumber.Coalesce(s.AltSiteNumber, "No Number");
string result = s.SiteNumber.Coalesce(string.IsNullOrWhiteSpace, s.AltSiteNumber, "No Number");
string navigationTitle = model?.NavigationTitle.
Coalesce(() => RemoteTitleLookup(model?.ID), () => model?.DisplayName);
Player player = player1.Coalesce(p => p?.Score > 0, player2, player3);
(PS:我想我在这里使用泛型有点跑题了。我想太多了吗?)
【讨论】:
以上是关于??合并空字符串?的主要内容,如果未能解决你的问题,请参考以下文章
freemarker合并单元格,ifelse标签的使用,null空字符串处理