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