5

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.

David Pine
  • 21,735
  • 6
  • 69
  • 98
  • 1
    You should put parenthesis, so `?.` operator known, where condition call chain ends: `(property?.AttributeProvider.GetAttributes(typeof(TAttribute), false).Any()).GetValueOrDefault()`. – user4003407 Jan 20 '17 at 02:21
  • If `property` is null, then the method will attempt to return null. However, it can't because the return type is `bool`, which is not a nullable type. Change the return type to `bool?`. – Abion47 Jan 20 '17 at 02:23

2 Answers2

6

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()
user4003407
  • 18,919
  • 2
  • 44
  • 58
2

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).

John Koerner
  • 35,668
  • 7
  • 74
  • 128
  • I know that I can you parentheses. But that is not what I'm asking. C# is treating the entire statement as `bool?` but I cannot act on it as if it is a `bool?`. I know what it is doing, I'm asking why. – David Pine Jan 20 '17 at 02:35