?? Coalesce for empty string?

C#StringCoalesceNull Coalescing-Operator

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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

Solution 2 - C#

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

Solution 3 - C#

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

Solution 4 - C#

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

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

Solution 5 - C#

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;

Solution 6 - C#

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

Solution 7 - C#

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

Solution 8 - C#

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.

Solution 9 - C#

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

Solution 10 - C#

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

Solution 11 - C#

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

Solution 12 - C#

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNick CraverView Question on Stackoverflow
Solution 1 - C#D'Arcy RittichView Answer on Stackoverflow
Solution 2 - C#Reed CopseyView Answer on Stackoverflow
Solution 3 - C#sfsrView Answer on Stackoverflow
Solution 4 - C#Justin MorganView Answer on Stackoverflow
Solution 5 - C#jjr2000View Answer on Stackoverflow
Solution 6 - C#wensveenView Answer on Stackoverflow
Solution 7 - C#ChilidogView Answer on Stackoverflow
Solution 8 - C#devioView Answer on Stackoverflow
Solution 9 - C#LoudenvierView Answer on Stackoverflow
Solution 10 - C#Nicholas PetersenView Answer on Stackoverflow
Solution 11 - C#PLopesView Answer on Stackoverflow
Solution 12 - C#claudiosfrView Answer on Stackoverflow