Question

I have different properties that can provide a value and I would like to return the first one that satisfies a specified condition.

The problem is when one of the sources is a class and is null, I do get a nice Object reference not set to an instance of an object..

I can't find a property to check against the nullity of the current provider, whether on the Func<T,bool> or the Expression<Func<Media,T>>.

Do you know how can I check against the nullity of a function target ?

public int TempoInteger
{
    get
    {
        double result;
        // In this case, AudioSummary is a class that can be null
        if (TryGetValue(out result, s => s > 0.0d, s => s.TempoBass, s => s.AudioSummary.Tempo))
        {
            return (int)Math.Round(result);
        }
        return -1;
    }
}

private AudioSummary AudioSummary {get; set;}

/// <summary>
/// Tries to get a value from multiple expressions, first expression value that satisfies the predicate will be returned. If no value satisifes the predicate the default value of <typeparamref name="T"/> will be returned.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="result"></param>
/// <param name="predicate"></param>
/// <param name="expressions"></param>
/// <returns></returns>
private bool TryGetValue<T>(out T result, Func<T, bool> predicate, params Expression<Func<Media, T>>[] expressions)
{
    foreach (var expression in expressions)
    {
        Func<Media, T> func = expression.Compile();

        // 'System.NullReferenceException' occurs here when AudioSummary is null
        T t = func(this);

        var b = predicate(t);
        if (b)
        {
            result = t;
            return true;
        }
    }
    result = default(T);
    return false;
}

public sealed class AudioSummary
{

    [JsonProperty("tempo")]
    public double Tempo { get; set; }

    // ...

}
Was it helpful?

Solution

There's several workaround for what you're trying to achieve.

  1. Wrap the expression invocation inside a try-catch block

    private bool TryGetValue<T>(
      out T result, 
      Func<T, bool> predicate, 
      params Expression<Func<Media, T>>[] expressions) 
    {
      ...
      T t;
      try
      {
        t = func(this);
      }
      catch (NullReferenceException)
      {
        t = default(T);
      }
      ...
    }
    
  2. Validate the expression for Null before passing it to TryGetValue()

    public int TempoInteger {
      get {
        double result;
    
        if (TryGetValue(out result, 
          s => s > 0.0d, 
          s => s.TempoBass, 
          s => s.AudioSummary != null ? s.AudioSummary.Tempo : 0.0d)) 
        {
          return (int)Math.Round(result);
        }
    
        return -1;
      }
    }
    
  3. Use the IfNotNull extension method, which does the same thing as point #2 but fancier

    public static class IfNotNullExtensionMethod
    {
      public static T2 IfNotNull<T1, T2>(this T1 t, Func<T1, T2> fn)
        where T1: class
      {
        return t != null ? fn(t) : default(T2);
      }
    }
    
    ...
    public int TempoInteger {
      get {
        double result;
    
        if (TryGetValue(out result, 
          s => s > 0.0d, 
          s => s.TempoBass, 
          s => s.AudioSummary.IfNotNull(x => x.Tempo))) 
        {
          return (int)Math.Round(result);
        }
    
        return -1;
      }
    }
    ...
    

    Credits for the IfNotNull extension methods goes to: here, here, and here

  4. Use the Maybe extensions method: maybe.codeplex.com. (disclaimer: I haven't tested this one, but it's mentioned in point #3 credits links)

OTHER TIPS

Before invoking TryGetValue() check for null objects s and AudioSummary because if AudioSummary is null you can't get to property AudioSummary.Tempo.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top