Null or NotNull

Published on Saturday, July 29, 2017

This topic is related to this question on StackOverflow. Let's discover, what are CanBeNull or NotNull attributes? How they work?

These attributes provided by JetBrains ReSharper Annotations and help you to find errors in your code with ReSharper.

First of all - definition of NotNullAttribute and CanBeNullAttribute:

namespace JetBrains.Annotations
{
  /// <summary>
  /// Indicates that the value of the marked element could never be <c>null</c>.
  /// </summary>
  /// <example><code>
  /// [NotNull] object Foo() {
  ///   return null; // Warning: Possible 'null' assignment
  /// }
  /// </code></example>
  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | 
                  AttributeTargets.Property | AttributeTargets.Field | 
                  AttributeTargets.Event | AttributeTargets.Interface | 
                  AttributeTargets.Parameter | AttributeTargets.Delegate | 
                  AttributeTargets.GenericParameter)]
  [Conditional("JETBRAINS_ANNOTATIONS")]
  public sealed class NotNullAttribute : Attribute
  {
  }
}
namespace JetBrains.Annotations
{
  /// <summary>
  /// Indicates that the value of the marked element could be <c>null</c> sometimes,
  /// so the check for <c>null</c> is necessary before its usage.
  /// </summary>
  /// <example><code>
  /// [CanBeNull] object Test() =&gt; null;
  /// 
  /// void UseTest() {
  ///   var p = Test();
  ///   var s = p.ToString(); // Warning: Possible 'System.NullReferenceException'
  /// }
  /// </code></example>
  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | 
                  AttributeTargets.Property | AttributeTargets.Field | 
                  AttributeTargets.Event | AttributeTargets.Interface | 
                  AttributeTargets.Parameter | AttributeTargets.Delegate | 
                  AttributeTargets.GenericParameter)]
  [Conditional("JETBRAINS_ANNOTATIONS")]
  public sealed class CanBeNullAttribute : Attribute
  {
  }
}

Their definition is equal and marked as Conditional("JETBRAINS_ANNOTATIONS"). This is important, when you create some library: if you don't define JETBRAINS_ANNOTATIONS variable, compiler will ignore all attributes and you won't get binary reference to JetBrains.Annotations.dll.

Sample project:

namespace NotNullProject
{
    class Program
    {
        static void Main([CanBeNull] string[] args)
        {
            var obj = new TestClass();

            string value = null;

            obj.Property = value;
            obj.Property = string.Empty;

            Console.WriteLine(obj.Property);
        }

        private class TestClass
        {
            private string _property;

            [NotNull]
            public string Property
            {
                get { return _property; }
                set { _property = value; }
            }
        }
    }
}

We have Property declared with NotNull attribute in TestClass. Let's create instance of the class and assign Property to null. This code compiles without errors. Maybe, we will get a runtime exception? No, NotNull attribute does not affect compilation process and does not add any additional runtime checks. But ReSharper tells us about potential problem:

NotNull

NotNull with condition checking:

static void Main([CanBeNull] string[] args)
{
    var obj = new TestClass();

    if (obj.Property == null)
    {
        Console.WriteLine("wrong");
    }
}

NotNull

ReSharper says that expression if always false because of NotNull attribute and can be removed. But this program will output wrong to console! So, be carefull. Annotating with NotNull does not guarantee that value is not null in runtime.

Can we make property that never returns null but can accept it?

public string Property
{
    [NotNull]
    get { return _property ?? string.Empty; }
    set { _property = value; }
}

NotNull

ReSharper does not tell anything. So, annotate property instead of getter or setter.

Attribute works inside property too:

[NotNull]
public string Property
{
    get
    {
        return null;
    }
    set
    {
        if (value == null)
        {
            Console.WriteLine("null");
        }
        _property = value;
    }
}

NotNull

NotNull

Setter is a method with one parameter. Can we tell ReSharper that this parameter is not null with attribute target?

public string Property
{
    get { return _property; }
    [param: NotNull]
    set { _property = value; }
}

NotNull

No, does not work;

Equivalent can be written with methods:

private class TestClass
{
    private string _property;

    public void SetProperty([NotNull] string value)
    {
        _property = value;
    }

    public string GetProperty()
    {
        return _property;
    }
}

NotNull

Conclusion

I expected more powerful support of usage these attributes with properties: atribute target, getters and setters annotationg. But we have to mark property only.