Using an expression to call a property and an object and determine if the object is null or not

I want to be able to call properties of objects that can be empty, but should not explicitly check if they are null or not when called.

Like this:

var something = someObjectThatMightBeNull.Property;

      

My idea is to create a method that takes an expression, something like this:

var something = GetValueSafe(() => someObjectThatMightBeNull.Property);

TResult? GetValueSafe<TResult>(Expression<Func<TResult>> expression) 
    where TResult : struct
{
    // what must I do?
}

      

What I need to do is check the expression and determine if it is someObjectThatMightBeNull

null or not. How should I do it?

If there is a smarter way to be lazy, I would appreciate it.

Thank!

+3


source to share


2 answers


It's tricky, but it can be done without leaving expression-earth:

// Get the initial property expression from the left 
// side of the initial lambda. (someObjectThatMightBeNull.Property)
var propertyCall = (MemberExpression)expression.Body;

// Next, remove the property, by calling the Expression 
// property from the MemberExpression (someObjectThatMightBeNull)
var initialObjectExpression = propertyCall.Expression;

// Next, create a null constant expression, which will 
// be used to compare against the initialObjectExpression (null)
var nullExpression = Expression.Constant(null, initialObjectExpression.Type);

// Next, create an expression comparing the two: 
// (someObjectThatMightBeNull == null)
var equalityCheck = Expression.Equal(initialObjectExpression, nullExpression);

// Next, create a lambda expression, so the equalityCheck 
// can actually be called ( () => someObjectThatMightBeNull == null )
var newLambda = Expression.Lambda<Func<bool>>(equalityCheck, null);

// Compile the expression. 
var function = newLambda.Compile();

// Run the compiled delegate. 
var isNull = function();

      



Speaking about it, as Andras Zoltan so eloquently added in the commentary: "Just because you can does not mean you should." Make sure you have a good reason for doing this. If there is a better way, then do it instead. Andras has a lot of workarounds.

+3


source


What you are talking about is called null dereferencing - this is SO specifically asking this question: the C # expression if-null-then-null .

Expressions are not really the answer (see below to clarify my reasons for this statement). This extension method can, however:

public static TResult? GetValueSafe<TInstance, TResult>(this TInstance instance,
  Func<TInstance, TResult> accessor)
  where TInstance : class
  where TResult : struct
{  
   return instance != null ? (TResult?)accessor(instance) : (TResult?)null;
}

      

And now you can do:

MyObject o = null;
int? i = o.GetValueSafe(obj => obj.SomeIntProperty);

Assert.IsNull(i);    

      

Obviously, this is most useful when the property is a structure; you can cast to any type and just use default(TResult)

- but then you get 0

for int, double, etc .:

public static TResult GetValueSafe<TInstance, TResult>(this TInstance instance,
  Func<TInstance, TResult> accessor, TResult def = default(TResult))
  where TInstance : class
{  
   return instance != null ? accessor(instance) : def;
}

      

This second version is more useful on purpose because it works for anyone TResult

. I added an additional parameter to allow the caller to provide a default value, for example (using o

from the previous code):

int i = o.GetValueSafe(obj => obj.SomeIntProperty); //yields 0
i = o.GetValueSafe(obj => obj.SomeIntProperty, -1); //yields -1

//while this yields string.Empty instead of null
string s = o.GetValueSafe(obj => obj.SomeStringProperty, string.Empty);

      

Edit - in response to David's comment

David assumed my answer is wrong because it does not provide an expression based solution, which is what was asked for. My point is that any truly correct and responsible answer on SO should always try to find a simpler solution for the person asking if it exists. I believe it is generally accepted that overly complex solutions to other simple problems should be avoided in our daily professional life; and SO is as popular as because the community behaves the same.

David also made a decision about my unsubstantiated claim that "they are not a solution", so I'm going to expand on that now and show why an expression-based solution is pretty much pointless, except for a rare edge that the OP doesn't actually ask (which, by the way, David's answer doesn't cover either).

The irony is that it makes this answer on its own, perhaps unnecessarily complex :) You can safely ignore from here if you really don't like why expressions aren't the best route

While it's correct to say that you can solve this with expressions, for the examples in the question, there is simply no reason to use them - it overcomplicates what is ultimately a fairly simple problem; and at runtime, the overhead of compiling the expression (and subsequently throwing it away if you haven't enabled caching, which would be tricky if you emit something like call sites like using DLR) is huge compared to the solution I'm here.



Ultimately the motivation for any decision is to try and keep the work required by the caller to a minimum, but at the same time, you also need to keep the work that needs to be done by the expression parser at the very least, otherwise the solution becomes almost insoluble without great work. To illustrate my point - let's look at the simplest that we can achieve with a static method that takes an expression given our object o

:

var i = GetValueSafe(obj => obj.SomeIntProperty);

      

Uh-oh, this expression doesn't really do anything because it doesn't convey to it o

- the expression itself is useless to us because we need an actual reference to o

which there might be null

. So the first solution to this, naturally, is to pass the reference explicitly:

var i = GetValueSafe(o, obj => obj.SomeIntProperty);

      

(Note - can also be written as extension method)

Thus, a static method assignment must take the first parameter and pass it to the compiled expression when it is called. It also helps to identify the type of expression whose property is being requested. However, it also completely nullifies the reason for using the expression in the first place; since the method itself can immediately decide whether to access the property or not - since it has a reference to an object that can be null

. So, in this case, it's easier, simpler, and faster to just pass a reference and access delegate (instead of an expression) like my extension method does.

As I mentioned, there is a workaround to pass an instance and that should do one of the following:

var i = GetValueSafe(obj => o.SomeIntProperty);

      

or

var i = GetValueSafe(() => o.SomeIntProperty);

      

We ditch the extension method version because with that we get the link passed to the method and once we get the link we can get rid of the expressions, as my last point proved.

Here we rely on the caller to understand that they have to include an expression that represents the actual instance (be it a visibility property or a field or a local variable) in the body of the expression, on the left side of the read member, so that we can actually get a specific from it. value to perform a null check.

This is not a natural use of expression parameters in the first place, so I think your callers might be confused. There's also another problem there, which I think would be a killer if you intend to use it a lot - you can't cache those expressions, because every time an instance whose "null" you want to bypass is baked into an expression which is transmitted. This means that you always have to recompile the expression for each call; and it will be very slow. If you parameterize an instance in an expression, you can cache it, but then you get the first solution that requires the instance to be passed; and again I've already shown that we can just use a delegate!

It's relatively easy - with the ExpressionVisitor class - to write something that can turn all property / field reads (and method calls for that matter) into "safe" calls, as you want. However, I do not see any use for it, if you do not intend to do about as safe reading: a.b.c.d

. But then increasing the value types to zero versions of themselves will give you a few headaches in rewriting the expressions I can tell you; leaving a solution that hardly anyone will understand :)

+3


source







All Articles