我发现自己越来越多地做的事情是检查字符串是否为空(如
""
或 null)和条件运算符。
当前示例:
s.SiteNumber.IsNullOrEmpty() ? "No Number" : s.SiteNumber;
这只是一个扩展方法,它相当于:
string.IsNullOrEmpty(s.SiteNumber) ? "No Number" : s.SiteNumber;
由于它是空的而不是 null,所以
??
不会起作用。 string.IsNullOrEmpty()
版本的 ??
将是完美的解决方案。我认为必须有一种更干净的方法来做到这一点(我希望!),但我一直找不到它。
有谁知道更好的方法来做到这一点,即使它只在 .Net 4.0 中?
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;
}
}
没有内置的方法可以做到这一点。但是,您可以使扩展方法返回字符串或 null,这将允许合并运算符起作用。然而,这会很奇怪,我个人更喜欢你目前的方法。
既然您已经在使用扩展方法,为什么不直接创建一个返回值或默认值的扩展方法:
string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
我知道这是一个老问题 - 但我一直在寻找答案,但以上都不符合我的需要以及我最终使用的:
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));
我有几个我喜欢使用的实用程序扩展:
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));
}
我只是使用 NullIfEmpty 扩展方法,如果字符串为空,则该方法将始终返回 null 允许 ?? (空合并运算符)正常使用。
public static string NullIfEmpty(this string s)
{
return string.IsNullOrEmpty(s) ? null : s;
}
这允许??正常使用并使链接易于阅读。
string string1 = string2.NullIfEmpty() ?? string3.NullIfEmpty() ?? string4;
空合并运算符的优点之一是它是短路的。当第一部分不为空时,不计算第二部分。当回退需要昂贵的操作时,这可能很有用。
我最终得到:
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);
也许是比之前提出的稍微快一点的扩展方法:
public static string Fallback(this string @this, string @default = "")
{
return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}
字符串扩展方法 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;
}
但没有尝试这些解决方案。
自从写了这个答案以来,我已经大大改进了我的“合并”方法,并且还对其进行了抽象,添加了泛型类型的版本。我的解决方案现在由以下三种方法组成:
public static string CoalesceWhiteSpace(this string str, params string[] strings) {
if (!string.IsNullOrWhiteSpace(str))
return str;
for (int i = 0; i < strings.Length; i++)
if (!string.IsNullOrWhiteSpace(strings[i]))
return strings[i];
return str;
}
public static T Coalesce<T>(this T v1, params T[] vs) {
if (v1 is not null)
return v1;
for (int i = 0; i < vs.Length; i++)
if (vs[i] is not null)
return vs[i];
return default;
}
public static T CoalesceDefault<T>(this T v1, params T[] vs) {
if (!EqualityComparer<T>.Default.Equals(v1, default))
return v1;
for (int i = 0; i < vs.Length; i++)
if (!EqualityComparer<T>.Default.Equals(vs[i], default))
return vs[i];
return v1;
}
CoalesceWhiteSpace
将接受任意数量的字符串与“源”字符串合并,将空格视为 null
。本文前一版本中使用的相同示例现在如下所示:
string s1 = null;
string s2 = " ";
string s3 = "loudenvier";
string s = s1.Coalesce(s2, s3);
Assert.AreEqual("loudenvier", s);
通用
Coalesce<T>
类似,但接受任何 nullable
类型。当然,它也可以与字符串一起使用:
string s = null;
var v = s.Coalesce(null, null, "outro");
Assert.AreEqual("outro", v);
但也适用于任何物体:
object o = null;
object expected = new();
var v = o.Coalesce(null, null, expected);
Assert.AreSame(expected, v);
最后,
CoalesceDefault
将通过与默认值进行比较来合并任意数量的对象(对于初始化为其默认值的非空类型很有用,如DateTime
):
DateTime d = default;
var now = DateTime.Now;
var v = d.CoalesceDefault(new DateTime(0), d, now);
Assert.AreEqual(now, v);
它也适用于字符串:
string s = " ";
var v = s.CoalesceDefault(null, null, "", "", null, " ", " ");
Assert.AreEqual(" ", v);
注意:null、"" 和 " " 被视为默认字符串值,因此上面仅返回最后一个带有多个空格的字符串!
原答案
我正在使用我自己的字符串合并扩展方法。由于这里使用的是 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);
我经常使用它。第一次使用后您就离不开的“实用”功能之一:-)
我喜欢以下扩展方法的简洁性
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
很容易将一些答案转换为具有泛型的辅助扩展类,以实现更广泛的用途:
注意:有关短路方法的说明,请参阅 wensveen 答案
// 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:我认为我在这里使用泛型有点偏离主题。我是不是想太多了?)
sqlCom.Parameters.Add(new SqlParameter("@qavCode", SqlDbType.Char, 11)).Value = (object)(string.IsNullOrEmpty(rf.Request.QavCode) ? null : rf.Request.QavCode) ?? DBNull.Value;
不知道,也许我回答这个问题太晚了,但在我的示例中,我将常规三元 ?: 混合到空合并运算符 ??为了让这个也能处理空字符串。 希望有帮助:)