views:

168

answers:

3

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?

+6  A: 

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.

Jon Skeet
It's ugly and confusing, but doesn't the null coalescing operator do the trick: `if( (object z = a ?? b ?? c) != null ) DoSomething();`
LBushkin
Another idea would be to pass a single lambda statement as an expression tree to a method that could dissassemble the expression tree and perform the short circuited evaluation.
LBushkin
I actually thought about that, believe it or not. Of course, for the given purpose -- checking nulls -- this would require approximately the same amount of code as the original approach and is therefore not all that useful. Naturally, for other purposes it may be better. The main reason I don't want to use it, though, is that it requires the use of lambdas, which are not available to VB (at least before VB 10, I think?), and I want something that would be useful from both C# and VB, since we use both languages at work.
Dan Tao
@LBushkin: ?? will only work if all the expressions are of the same type (modulo nullity) or one type can be converted to another. I don't think it would work in the case given here, for example.
Jon Skeet
@Jon: Yes, you're right about ??. One would have to cast each element to object to make it work, which doesn't necessarily make it simpler: `if( (object z = (object)a ?? (object)b ?? (object)c ) != null ) DoSomething();`
LBushkin
Dan Tao
@Jon: What are your thoughts on the expression tree transformation idea. I threw it out there, but I'm not even 100% sure it's feasible given some of the restrictions on what is allowed in an expression tree.
LBushkin
@Jon: Unless I'm mistaken, this is the call-by-name convention of Algol. No?
Mike Dunlavey
@Mike: Couldn't say, I'm afraid. @LBushkin: Yes, it should be feasible. The implementation could be pretty ugly, but it would be feasible :)
Jon Skeet
+1  A: 

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".

LBushkin
You don't need to make it three separate arguments - just use `() => obj.Parameters.UserSettings` and get the expression tree to check the result for null between each property invocation.
Jon Skeet
A: 

You could use reflections if you don't mind losing static type safety. I do mind, so I just use your first short-circuiting construction. A feature like Eric mentioned would be welcome :)

I've thought about this problem a few times. Lisp has macros that solve the problem in the way you mentioned, since they allow you to customize your evaluation.

I have also tried using extension methods to solve this problem, but nothing there is less ugly than the original code.

Edit: (Replies don't let me insert code blocks, so editing my post)

Oops, didn't keep up on this. Sorry about that :)

You can use reflections to look up and evaluate a member or property via a string. A class one of my friends wrote took a syntax like:

new ReflectionHelper(obj)["Parameters"]["UserSettings"]

It worked via method chaining, returning a ReflectionHelper at each level. I know that NullReferenceException is a problem in that example. I just wanted to demonstrate how evaluation can be deferred to runtime.

An example slightly closer to being helpful:

public class Something
{
  public static object ResultOrDefault(object baseObject, params string[] chainedFields)
  {
    // ...
  }
}

Again, this syntax stinks. But this demonstrates using strings + reflections to defer evaluation to runtime.

Merlyn Morgan-Graham
Out of curiosity, how could one use reflection to achieve this? The problem seems to be that once you've entered the method, all of its arguments have already been evaluated. I'm not clear on how reflection could remedy this; but maybe I'm missing something.
Dan Tao
Edited the post to reply to your comment
Merlyn Morgan-Graham