Can I force my own short circuit 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 } 

This is a tempting prospect to write a helper function to accept a variable number of arguments and simplify this 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, I will get a NullReferenceException when I try to pass obj.Parameters to NoNulls .

Thus, the above approach is clearly erroneous. But the if , using the && operator, works great because it is shorted. So: is there a way to make the method short-circuited so that its arguments are not evaluated until explicitly specified in the method?

+5
source share
3 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 name it with

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

Basically, you give delegates the ability to evaluate values ​​lazily rather than the values ​​themselves (since evaluating these values ​​throws an exception).

I do not say it well, but it is there as an option ...

EDIT: This actually (and by accident) comes to the point of what Dan was after, I think. All arguments to the method are evaluated before the method is executed. Using delegates effectively allows you to defer this evaluation until the method calls the delegate to get the value.

+9
source

You can write a function that takes an expression tree and converts that tree to a form that will check for zeros, and return a Func<bool> that could be safely evaluated to determine if zero exists.

I suspect that although the resulting code can be cool, it will be confusing and much less efficient than just writing a bunch of short-circuited checks a != null && ab != null... In fact, this is more likely to be less efficient than just checking all values ​​and NullReferenceException (not that I protect exception handling as a thread of a control mechanism).

The signature for such a function will look something like this:

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

and it will look something like this:

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

If I get some free time, I will write a function that performs just such an expression tree conversion and updates my post. However, I am sure that John Skeet or Mark Gravell could write such a function with one eye closed and one hand behind his back.

I would also like to see C # implement the operator .? referenced by Eric. As another Eric (Cartman) can say, it will be a “kick ass."

+1
source

You can use reflections if you don't mind losing security of a static type. I don't mind, so I just use your first squirrel cage design. You can mention a feature like Eric :)

I have thought about this issue several times. Lisp has macros that solve the problem as you mentioned, as they allow you to customize your score.

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

Edit: (Answers do not allow me to insert blocks of code, so editing my post)

Oops, didn't keep up with that. Sorry for this:)

You can use reflections to search and evaluate an element or property through a string. One class from my friends wrote syntax like:

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

It worked through a chain of methods, returning a ReflectionHelper at each level. I know that NullReferenceException is the problem in this example. I just wanted to demonstrate how an evaluation can be delayed until runtime.

An example is a bit closer to utility:

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

Again, this syntax stinks. But this demonstrates the use of lines + reflections to defer evaluation to runtime.

0
source

Source: https://habr.com/ru/post/906159/


All Articles