??合并空字符串?

发布于 2024-08-24 06:05:59 字数 473 浏览 11 评论 0原文

我发现自己越来越多地做的事情是检查字符串是否为空(如 "" 或 null)和条件运算符。

当前的示例:

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

这只是一个扩展方法,它相当于:

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

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

有谁知道更好的方法来做到这一点,即使它只在 .Net 4.0 中?

Something I find myself doing more and more is checking a string for empty (as in "" or null) and a conditional operator.

A current example:

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

This is just an extension method, it's equivalent to:

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

Since it's empty and not null, ?? won't do the trick. A string.IsNullOrEmpty() version of ?? would be the perfect solution. I'm thinking there has to be a cleaner way of doing this (I hope!), but I've been at a loss to find it.

Does anyone know of a better way to do this, even if it's only in .Net 4.0?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

漫雪独思 2024-08-31 06:05:59

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;
    }
}

C# already lets us substitute values for null with ??. So all we need is an extension that converts an empty string to null, and then we use it like this:

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

Extension class:

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;
    }
}
薄暮涼年 2024-08-31 06:05:59

没有内置的方法可以做到这一点。但是,您可以使扩展方法返回字符串或 null,这将允许合并运算符起作用。然而,这会很奇怪,我个人更喜欢你目前的方法。

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

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

There isn't a built-in way to do this. You could make your extension method return a string or null, however, which would allow the coalescing operator to work. This would be odd, however, and I personally prefer your current approach.

Since you're already using an extension method, why not just make one that returns the value or a default:

string result = s.SiteNumber.ConvertNullOrEmptyTo("No Number");
属性 2024-08-31 06:05: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));

I know this is an old question - but I was looking for an answer and none of the above fit my need as well as what I ended up using:

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

Usage:

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

EDIT:
An even more compact way of writing this function would be:

static string Coalesce(params string[] strings) => strings.FirstOrDefault(s => !string.IsNullOrEmpty(s));
永言不败 2024-08-31 06:05:59

我有几个我喜欢使用的实用程序扩展:

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));
}

I have a couple of utility extensions that I like to use:

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;
}

You could also use this if you want it to apply to any type:

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

Edit: Inspired by sfsr's answer, I'll be adding this variant to my toolbox from now on:

public static string Coalesce(this string str, params string[] strings)
{
    return (new[] {str})
        .Concat(strings)
        .FirstOrDefault(s => !string.IsNullOrEmpty(s));
}
つ低調成傷 2024-08-31 06:05:59

我只是使用 NullIfEmpty 扩展方法,如果字符串为空,则该方法将始终返回 null 允许 ?? (空合并运算符)正常使用。

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

这然后允许?正常使用并使链接易于阅读。

string string1 = string2.NullIfEmpty() ?? string3.NullIfEmpty() ?? string4;

I simply use a NullIfEmpty extension method which will always return null if the string is empty allowing ?? (Null Coalescing Operator) to be used as normal.

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

This then allows ?? to be used as normal and makes chaining easy to read.

string string1 = string2.NullIfEmpty() ?? string3.NullIfEmpty() ?? string4;
空宴 2024-08-31 06:05:59

空合并运算符的优点之一是它是短路的。当第一部分不为空时,不计算第二部分。当回退需要昂贵的操作时,这可能很有用。

我最终得到:

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);

One of the advantages of the null-coalescing operator is that it short-circuits. When the first part isn't null, the second part isn't evaluated. This can be useful when the fallback requires an expensive operation.

I ended up with:

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

Usage:

string navigationTitle = model?.NavigationTitle.
    Coalesce(() => RemoteTitleLookup(model?.ID)). // Expensive!
    Coalesce(() => model?.DisplayName);
没有伤那来痛 2024-08-31 06:05:59

可能比之前提出的扩展方法稍快一些:

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

A slightly faster extension method than proposed earlier perhaps:

public static string Fallback(this string @this, string @default = "")
{
    return (@this == null || @this.Trim().Length == 0) ? @default : @this;
}
独孤求败 2024-08-31 06:05:59

字符串扩展方法 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;
}

但没有尝试这些解决方案。

how about a string extension method ValueOrDefault()

public static string ValueOrDefault(this string s, string sDefault)
{
    if (string.IsNullOrEmpty(s))
        return sDefault;
    return s;
}

or return null if string is Empty:

public static string Value(this string s)
{
    if (string.IsNullOrEmpty(s))
        return null;
    return s;
}

Didn't try these solutions though.

屌丝范 2024-08-31 06:05:59

自从写这个答案以来,我已经大大改进了我的“合并”方法,并且还抽象了它,添加了泛型类型的版本。我的解决方案现在由以下三个方法组成:

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 类似,但接受任何 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;
    }
}

我认为这非常简单,您甚至不需要为 null 烦恼字符串值。像这样使用它:

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

我经常使用它。第一次使用后您就离不开的“实用”功能之一:-)

Since writing this answer I've improved my "coalesce" method considerably, and also abstracted it adding versions for generic types. My solution now consists of the three methods bellow:

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;
}

The CoalesceWhiteSpace will accept an arbitrary number of strings to coalesce with a "source" string, considering whitespace as null. The same example used on the previous version of this post now looks like this:

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

The generic Coalesce<T> is similar but accepts any nullable type. And, of course, it can be used with strings also:

string s = null;
var v = s.Coalesce(null, null, "outro");
Assert.AreEqual("outro", v);

But also with any object:

object o = null;
object expected = new();
var v = o.Coalesce(null, null, expected);
Assert.AreSame(expected, v);

And, finally, the CoalesceDefault will coalesce an arbitrary number of objects by comparing it with it's default value (useful to use with non-nullable types which are initialized to their default values, like DateTime):

DateTime d = default;
var now = DateTime.Now;
var v = d.CoalesceDefault(new DateTime(0), d, now);
Assert.AreEqual(now, v);

It also works with strings:

string s = "  ";
var v = s.CoalesceDefault(null, null, "", "", null, " ", "   ");
Assert.AreEqual("  ", v);

Note: null, "" and " " are considered default string values, and, as such only the last string with multiple spaces is returned above!


ORIGINAL ANSWER

I'm using a string Coalesce extension method of my own. Since those here are using LINQ, and absolutelly wasting resources for time intensive operations (I'm using it in tight loops), I'll share mine:

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

I think it is quite simple, and you don't even need to bother with null string values. Use it like this:

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

I use it a lot. One of those "utility" functions you can't live without after first using it :-)

情话已封尘 2024-08-31 06:05:59

我喜欢下面的扩展方法 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

I like the brevity of the following extension method QQQ for this, though of course an operator like? would be better. But we can 1 up this by allowing not just two but three string option values to be compared, which one encounters the need to handle every now and then (see second function below).

#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
雄赳赳气昂昂 2024-08-31 06:05:59

很容易将一些答案转换为具有泛型的辅助扩展类,以实现更广泛的用途:

注意:有关短路方法的说明,请参阅 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:我想我在这里使用泛型有点偏离主题。我是不是想太多了?)

It's easy to turn some of the answers in to a helper extension class with generics for a even broader usage:

NOTE: for an explanation of short-circuit method the see the 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;
}

Example usage:

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: I think I'm getting bit off topic here using generics. Am I overthinking this?)

苍暮颜 2024-08-31 06:05:59
sqlCom.Parameters.Add(new SqlParameter("@qavCode", SqlDbType.Char, 11)).Value = (object)(string.IsNullOrEmpty(rf.Request.QavCode) ? null : rf.Request.QavCode) ?? DBNull.Value;

不知道,也许我回答这个问题太晚了,但在我的示例中,我将常规三元 ?: 混合到空合并运算符 ??为了让这个也能处理空字符串。
希望它有帮助:)

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

Don't know, maybe I'm too late answering this question but in my example I mixed the regular ternary ?: to the null-coalescing operator ?? in order this one to work with empty strings too.
Hope it helps :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文