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

3

5

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?

Faefaeces answered 16/12, 2009 at 20:40 Comment(7)
What you actually need to solve this problem is not lazy evaluation of method arguments -- though, as you note, that would do the trick. What would be better is an operator x.?y that has the semantics of (x == null ? null : x.y) -- that way you can say "if (obj.?Parameters.?UserSettings == null)". We considered such an operator for C# 4 but never implemented it. Perhaps for a hypothetical future version.Perdurable
That's really interesting. Are there any other languages that implement an equivalent operator?Miamiami
@Chris: Groovy does. It's called the null-safe dereferencing operator. It's good to hear that the C# team has considered it and might consider it again :)Archive
Note that all implementations here (except the one using reflections, wink wink) still have to write: obj, obj.Parameters, obj.Parameters.UserSettings. Really, without the operator Eric mentions, the original short circuit && is the least confusing and most performant.Floydflss
The Ruby gem andand provides this kind of 'guarded method invocation': andand.rubyforge.org The 'background' section has a link to raganwald's original post about itHoitytoity
This for some reason reminds me of quoting code in Lisp for later evaluation.Heliostat
It reminds you of that because that's what it is! As is usually the case, the designers of Lisp anticipated the need and implemented it back in the 1950's.Perdurable
A
9

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.

Archive answered 16/12, 2009 at 20:45 Comment(9)
It's ugly and confusing, but doesn't the null coalescing operator do the trick: if( (object z = a ?? b ?? c) != null ) DoSomething();Eldreeda
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.Eldreeda
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.Faefaeces
@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.Archive
@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();Eldreeda
@LBushkin: I think the more pertinent point to make here is that using the null coalesce operator is actually (arguably) less readable than using if with a bunch of && operators, which would even be OK if it were applicable to more scenarios than just checking for nulls. But what I was actually wondering about is whether writing a short-circuited method in general is possible (even though my example did involve null checking).Faefaeces
@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.Eldreeda
@Jon: Unless I'm mistaken, this is the call-by-name convention of Algol. No?Arctogaea
@Mike: Couldn't say, I'm afraid. @LBushkin: Yes, it should be feasible. The implementation could be pretty ugly, but it would be feasible :)Archive
E
1

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

Eldreeda answered 16/12, 2009 at 21:21 Comment(1)
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.Archive
F
0

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.

Floydflss answered 16/12, 2009 at 21:21 Comment(2)
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.Faefaeces
Edited the post to reply to your commentFloydflss

© 2022 - 2024 — McMap. All rights reserved.