C# if-null-then-null expression

2019-01-08 11:39发布

问题:

Just for curiosity/convenience: C# provides two cool conditional expression features I know of:

string trimmed = (input == null) ? null : input.Trim();

and

string trimmed = (input ?? "").Trim();

I miss another such expression for a situation I face very often:

If the input reference is null, then the output should be null. Otherwise, the output should be the outcome of accessing a method or property of the input object.

I have done exactly that in my first example, but (input == null) ? null : input.Trim() is quite verbose and unreadable.

Is there another conditional expression for this case, or can I use the ?? operator elegantly?

回答1:

Something like Groovy's null-safe dereferencing operator?

string zipCode = customer?.Address?.ZipCode;

I gather that the C# team has looked at this and found that it's not as simple to design elegantly as one might expect... although I haven't heard about the details of the problems.

I don't believe there's any such thing in the language at the moment, I'm afraid... and I haven't heard of any plans for it, although that's not to say it won't happen at some point.

EDIT: It's now going to be part of C# 6, as the "null-conditional operator".



回答2:

You can choose between a custom Nullify class or a NullSafe extension method as described here: http://qualityofdata.com/2011/01/27/nullsafe-dereference-operator-in-c/

The usage will be as follows:

//Groovy:
bossName = Employee?.Supervisor?.Manager?.Boss?.Name

//C# Option 1:
bossName = Nullify.Get(Employee, e => e.Supervisor, s => s.Manager,
                       m => m.Boss, b => b.Name);
//C# Option 2:
bossName = Employee.NullSafe( e => e.Supervisor ).NullSafe( s => s.Boss )
                      .NullSafe( b => b.Name );


回答3:

Currently we can only write an extension method if you don't want to repeat yourself, I'm afraid.

public static string NullableTrim(this string s)
{
   return s == null ? null : s.Trim();
}


回答4:

As a workaround you can use this which is based on Maybe monad.

public static Tout IfNotNull<Tin, Tout>(this Tin instance, Func<Tin, Tout> Output)
{
    if (instance == null)
        return default(Tout);
    else
        return Output(instance);
}

Use it this way:

int result = objectInstance.IfNotNull(r => 5);
var result = objectInstance.IfNotNull(r => r.DoSomething());


回答5:

There's nothing built-in, but you could wrap it all up in an extension method if you wanted (although I probably wouldn't bother).

For this specific example:

string trimmed = input.NullSafeTrim();

// ...

public static class StringExtensions
{
    public static string NullSafeTrim(this string source)
    {
        if (source == null)
            return source;    // or return an empty string if you prefer

        return source.Trim();
    }
}

Or a more general-purpose version:

string trimmed = input.IfNotNull(s => s.Trim());

// ...

public static class YourExtensions
{
    public static TResult IfNotNull<TSource, TResult>(
        this TSource source, Func<TSource, TResult> func)
    {
        if (func == null)
            throw new ArgumentNullException("func");

        if (source == null)
            return source;

        return func(source);
    }
}


回答6:

I had the same problem I wrote a few little extension methods:

public static TResult WhenNotNull<T, TResult>(
    this T subject, 
    Func<T, TResult> expression)
    where T : class
{
    if (subject == null) return default(TResult);
    return expression(subject);
}

public static TResult WhenNotNull<T, TResult>(
    this T subject, Func<T, TResult> expression,
    TResult defaultValue)
    where T : class
{
    if (subject == null) return defaultValue;
    return expression(subject);
}

public static void WhenNotNull<T>(this T subject, Action<T> expression)
    where T : class
{
    if (subject != null)
    {
        expression(subject);
    }
}

You use it like this;

string str = null;
return str.WhenNotNull(x => x.Length);

or

IEnumerable<object> list;
return list.FirstOrDefault().WhenNotNull(x => x.id, -1);

or

object obj;
IOptionalStuff optional = obj as IOptionalStuff;
optional.WhenNotNull(x => x.Do());

There are also overloads for nullable types.