Thomas C. G. de Vilhena
Thomas C. G. de Vilhena

Reputation: 14565

How to validate lambda expressions against null references?

I have a method that accepts a generic delegate as a parameter, and inserts it in a list:

public void AddFilterMember<T>(Func<T, bool> filterMember)
{
    filter.Add(filterMember);
}

Later on all delegates are invoked over an instance of type T to find out if this instance passes the filter, i.e, if true is returned for every filterMember invoked.

I noticed that it is possible to pass an invalid lambda expression like the following:

string str = null;
AddFilterMember(x => str.Contains((string)x));

Which obviously will throw an exception when invoked because the str string is null. So I would like to know the best way to validate a lambda expression against null references (other than its parameters) at the moment it is defined?

I guess one option would be to invoke it using a default instance of T, but sometimes this is not feasible because T may not have a default parameterless constructor.

Thanks in advance!

Upvotes: 0

Views: 3400

Answers (3)

Jesse Chisholm
Jesse Chisholm

Reputation: 4025

Related question and possible answer:

Reflection - Get the list of method calls inside a lambda expression

But without try..catch around an actual execution, it is an NP-complete problem to decide whether or not a lambda expression is going to throw later on.

Especially if, like in your example, the lambda refers to a variable that may or may not be null each time the expression is executed. If the lambda doesn't check, then it might throw.

re: some T don't have a default value.

Perhaps a cast: (T)null for the try..catch test of the lambda, since you were specifically asking about NullReferenceException.

-Jesse

Upvotes: 0

Tim S.
Tim S.

Reputation: 56536

Here's a possible solution:

public bool AddFilterMember<T>(Func<T, bool> filterMember, T checkValue = default(T))
{
    try
    {
        filterMember(checkValue);
    }
    catch
    {
        return false;
    }
    filter.Add(filterMember);
    return true;
}

If the caller knows that default(T) won't work, but expects all actually-used values to work, they can specify the checkValue to be an example. From there, you just try to run the delegate and see if it works or not. A bool is returned to let the caller know whether it was successfully done or not.

Note that invoking the delegate can cause side effects. The behavior of this should be documented so callers aren't surprised.

Upvotes: 1

GETah
GETah

Reputation: 21409

I usually do that this way:

AddFilterMember(
     x => {
           if(str == null) 
             throw new ArgumentNullException("str cannot be null");                     
           str.Contains((string)x)
          });

Upvotes: 1

Related Questions