Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I force my own short-circuiting in a method call?

Suppose I want to check a bunch of objects to make sure none is null:

if (obj != null &&
    obj.Parameters != null &&
    obj.Parameters.UserSettings != null) {

    // do something with obj.Parameters.UserSettings
}

It is an alluring prospect to write a helper function to accept a variable number of arguments and simplify this kind of check:

static bool NoNulls(params object[] objects) {
    for (int i = 0; i < objects.Length; i++)
        if (objects[i] == null) return false;

    return true;
}

Then the above code could become:

if (NoNulls(obj, obj.Parameters, obj.Parameters.UserSettings)) {
    // do something
}

Right? Wrong. If obj is null, then I'll get a NullReferenceException when I try to pass obj.Parameters to NoNulls.

So the above approach is clearly misguided. But the if statement using the && operator works just fine since it is short-circuited. So: is there any way to make a method short-circuited, so that its arguments are not evaluated until explicitly referenced within the method?

like image 623
Dan Tao Avatar asked Dec 16 '09 20:12

Dan Tao


2 Answers

Well, this is ugly but...

static bool NoNulls(params Func<object>[] funcs) {
    for (int i = 0; i < funcs.Length; i++)
        if (funcs[i]() == null) return false;

    return true;
}

Then call it with:

if (NoNulls(() => obj,
            () => obj.Parameters,
            () => obj.Parameters.UserSettings)) {
    // do something
}

Basically you're providing delegates to evaluate the values lazily, rather than the values themselves (as evaluating those values is what causes an exception).

I'm not saying it's nice, but it's there as an option...

EDIT: This actually (and accidentally) gets to the heart of what Dan was after, I think. All a method's arguments are evaluated before the method itself is executed. Using delegates effectively lets you delay that evaluation until the method needs to call the delegate to retrieve the value.

like image 196
Jon Skeet Avatar answered Sep 30 '22 04:09

Jon Skeet


You could write a function that accepts an expression tree and transforms that tree into a form that will check for nulls, and return a Func<bool> that could be safely evaluated to determine if there is a null.

I suspect that while the resulting code may be cool, it would be confusing, and much less performant than just writing a bunch of short-circuited a != null && a.b != null... checks. In fact, it would likely be less performant than just checking all the values and catching the NullReferenceException (not that I advocate for exception handling as a flow of control mechanism).

The signature for such a function would be something like:

public static Func<bool> NoNulls( Expression<Func<object>> expr )

and it's usage would look something like:

NoNulls( () => new { a = obj, 
                     b = obj.Parameters, 
                     c = obj.Parameters.UserSettings } )();

If I get some free time, I will write a function that does just such an expression tree transformation and update my post. However, I'm sure that Jon Skeet or Mark Gravell could write such a function with one eye closed and one hand behind their back.

I would also love to see C# implement the .? operator that Eric alludes to. As a different Eric (Cartman) might say, that would "kick ass".

like image 37
LBushkin Avatar answered Sep 30 '22 03:09

LBushkin