208

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?

gdoron
  • 147,333
  • 58
  • 291
  • 367
Nick Craver
  • 623,446
  • 136
  • 1,297
  • 1,155
  • 13
    Just to tantalize you a bit, you can easily define custom, ad-hoc binary (and unary, for that matter) operators in F#. Here `let (|?) x y = if String.IsNullOrEmpty(x) then y else x` and use it like `s.SiteNumber |? "No Number"`. – Stephen Swensen Apr 01 '12 at 00:49

12 Answers12

205

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;
    }
}
D'Arcy Rittich
  • 167,292
  • 40
  • 290
  • 283
85

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");
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 8
    I think you're right, and this is the cleanest solution currently available that's still readable. I'd love something like a `???` operator in C# 5 though, who knows. – Nick Craver Mar 10 '10 at 21:01
  • 2
    and what would the ??? operator do? take default values in addition to nulls? sounds extremely complicated at best – bevacqua Jul 25 '11 at 00:09
  • 1
    With lambda expressions maybe? For instance: assume "item" is nullable, then... `item ?? x=> x.ToString() : null;` – Isaac Llopis Mar 06 '14 at 11:04
  • @IsaacLlopis that ends up looking messy-er than OP's original snippet – maraaaaaaaa Dec 04 '15 at 14:39
  • I would prefer a name like `ReplaceNullOrEmptyBy()` because it's not conversion. But the concept itself has a weakness: It does not work if the `string?` variable should be replaced not by a string literal, but by another one or multiple chained `string?` variables, and finally by a string literal. The chained calls will create nullability warnings again. – Tobias Knauss Feb 10 '23 at 08:32
58

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));
user489998
  • 4,473
  • 2
  • 29
  • 35
sfsr
  • 1,091
  • 1
  • 10
  • 9
20

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));
}
Justin Morgan - On strike
  • 30,035
  • 12
  • 80
  • 104
  • 2
    I'm definitely using the "Coalesce" term, as it more closely resembles the intent of the null coalescing operator (??), though I changed it to "CoalesceTo." – llaughlin May 31 '12 at 21:10
  • What does the `@` prefix on the `@default` parameter do? I've never seen that before. – Drew Chapin Dec 14 '16 at 20:46
  • 3
    @druciferre - That just allows you to use `default` as a variable name even though it's a reserved keyword in C#. – Justin Morgan - On strike Dec 28 '16 at 15:31
  • Why do you call this Coalesce when it doesn't bring the values together, but merely selects the one that isn't empty? It's a confusing name dude. – Jimmyt1988 Jan 10 '17 at 11:41
  • 1
    @Jimmyt1988 - Because it approximates the standard T-SQL [COALESCE](https://msdn.microsoft.com/en-us/library/ms190349.aspx) function. – Justin Morgan - On strike Jan 10 '17 at 16:28
  • 2
    @Jimmyt1988 - Also because it specifically selects the *first* non-empty function in an arbitrary-length list. It's a subtle detail, but the T-SQL function works the same way. The name makes it intuitive to anyone who knows that function, with or without documentation. – Justin Morgan - On strike Jan 10 '17 at 16:43
9

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;
jjr2000
  • 547
  • 8
  • 20
7

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);
wensveen
  • 783
  • 10
  • 20
6

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;
}
Chilidog
  • 61
  • 1
  • 1
  • 12
    Why not make use of [IsNullOrWhitespace](http://msdn.microsoft.com/en-us/library/system.string.isnullorwhitespace.aspx) rather than trim and length it. – weston Dec 19 '12 at 15:11
  • 1
    `code` public static string Coalesce(this string @this, string @default = "") { return (@this == null || String.IsNullOrWhiteSpace(@this)) ? @default : @this; } – paul-2011 Jan 23 '16 at 14:41
4

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.

devio
  • 36,858
  • 7
  • 80
  • 143
  • 2
    I like option #1 there, although I'd call it something more semantic like Or(), so I could write "string s = s.SiteNumber.Or("Default");" – Jerod Venema Apr 29 '11 at 19:25
  • 2
    Calling something `...OrDefault()` would be confusing if it didn't behave like the rest of the framework's `...OrDefault()` methods. Like it or not, MS has given a specific meaning to that naming and deviating from that behavior in custom methods is unnecessarily confusing to users of your API. – mattmc3 Jul 09 '11 at 23:12
3

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

Loudenvier
  • 8,362
  • 6
  • 45
  • 66
  • I don't think you understand why they are using LINQ, and since parameters are evaluated before calls, your `s2.Coalesce(s3)` is run even when not needed as well. Better to use a `NullIfEmpty()` extension and `??`. – NetMage Apr 16 '20 at 20:42
  • @NetMage I can guarantee you that the LINQ versions are a lot less performant than the one I presented. You can create a simple benchmark to test this if you wish. I suggest using https://github.com/dotnet/BenchmarkDotNet to prevent common pitfalls when writing benchmarking code. – Loudenvier Apr 21 '20 at 16:53
0

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
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Nicholas Petersen
  • 9,104
  • 7
  • 59
  • 69
  • If you like short names you could call it `Or`, and I would use the `params` keyword, like the other answers, which avoids duplicate definitions for multiple parameters. – chtenb Jul 11 '19 at 14:18
  • Thanks for the idea. I have long since replaced this name with "FirstNotNull" in my own usage. On `params`, it would be better not to do that for the default scenario or two, as `params` causes an array to be allocated needlessly when you only have the default one or two inputs. After that makes sense. – Nicholas Petersen Jul 11 '19 at 14:58
0

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

PLopes
  • 93
  • 2
  • 6
0
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 :)

claudiosfr
  • 146
  • 1
  • 5