Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is null propagation inconsistently propagating Nullable<T>?

I'm specifically calling attention to null-propagation as it pertains to bool? and the use of a bool returning method. For example, consider the following:

public static bool IsAttributedWith<TAttribute>(this JsonProperty property) 
    where TAttribute : Attribute
{
    return property?.AttributeProvider
                    .GetAttributes(typeof(TAttribute), false)
                    .Any();
}

This doesn't compile, and the following error exists:

Cannot implicitly convert bool? to bool. An explicit conversion exists (are you missing a cast)?

This implies that it is treating the entire body of the method as a bool?, as such I would assume that I could say .GetValueOrDefault() after the .Any() but this is not allowed as .Any() returns bool not bool?.

I know that I could do either of the following as a work around:

public static bool IsAttributedWith<TAttribute>(this JsonProperty property) 
    where TAttribute : Attribute
{
    return property?.AttributeProvider
                    .GetAttributes(typeof(TAttribute), false)
                    .Any()
        ?? false;
}

Or

public static bool IsAttributedWith<TAttribute>(this JsonProperty property) 
    where TAttribute : Attribute
{
    var any = 
        property?.AttributeProvider
                 .GetAttributes(typeof(TAttribute), false)
                 .Any();

     return any.GetValueOrDefault();
}

Or

public static bool IsAttributedWith<TAttribute>(this JsonProperty property) 
    where TAttribute : Attribute
{
    return property?.AttributeProvider
                    .GetAttributes(typeof(TAttribute), false)
                    .Any()
        ?? false;
}

My question is, why can I not directly invoke .GetValueOrDefault() chaining on the .Any() invocation?

public static bool IsAttributedWith<TAttribute>(this JsonProperty property) 
    where TAttribute : Attribute
{
    return (property?.AttributeProvider
                    .GetAttributes(typeof(TAttribute), false)
                    .Any())
                    .GetValueOrDefault();
}

I think this would make sense as the value is actually bool? at this point and not bool.

like image 383
David Pine Avatar asked Jan 20 '17 02:01

David Pine


2 Answers

After ?. operator all following call chain interpreted as conditional not only immediate call. So, this code:

property?.AttributeProvider
         .GetAttributes(typeof(TAttribute), false)
         .Any()

interpreted as

property==null ? (bool?)null : property.AttributeProvider
                                       .GetAttributes(typeof(TAttribute), false)
                                       .Any()

If you add GetValueOrDefault():

property==null ? (bool?)null : property.AttributeProvider
                                       .GetAttributes(typeof(TAttribute), false)
                                       .Any()
                                       .GetValueOrDefault()

it will fail, because Any() return bool not bool?. Thus you need to use parenthesis here:

(property==null ? (bool?)null : property.AttributeProvider
                                        .GetAttributes(typeof(TAttribute), false)
                                        .Any())
                                        .GetValueOrDefault()

Same parenthesis you need to use when you use ?. operator:

(property?.AttributeProvider
          .GetAttributes(typeof(TAttribute), false)
          .Any())
          .GetValueOrDefault()
like image 191
user4003407 Avatar answered Nov 20 '22 06:11

user4003407


The GetValueOrDefault call is executing on the return of the Any() method, which returns a bool. If you want to execute on the result of the whole body, you would have to wrap it in parentheses.

 return (property?.AttributeProvider
                .GetAttributes(typeof(TAttribute), false)
                .Any())
                .GetValueOrDefault();

The null conditional operator is a short circuiting operator and thus anything to the right of the dot that is attempting to execute on the object or any its properties or methods will not be executed if the object is null. So, in order to execute code on the whole statement, you must wrap it some way (parentheses or using another object).

like image 43
John Koerner Avatar answered Nov 20 '22 07:11

John Koerner