C# 8: The output of Nullable Reference Types and how to reflect it

September 28, 2018, (updated on October 15, 2018), 2 comments, Software Development

After reading some articles about the new C# 8 feature Nullable Reference Types, one question still was unanswered: How does a compiled assembly with nullable types enabled look like and how can I access the nullability information via reflection? Because I couldn’t find an article with an answer, I had to figure it out myself.

First I wrote a simple class which I wanted to analyze:

[module: System.Runtime.CompilerServices.NonNullTypes(true)]

class Test
{
    public string? Foo {get; set;}

    public void Bar(string? baz, string buz) 
    {

    }
}

With the help of sharplab.io I compiled it with C# 8 and looked at the resulting output (simplified):

[module: NonNullTypes(true)]
internal class Test
{
    [Nullable]
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string <Foo>k__BackingField;

    [Nullable]
    public string Foo
    {
        [CompilerGenerated]
        [return: Nullable]
        get
        {
            return <Foo>k__BackingField;
        }
        [CompilerGenerated]
        [param: Nullable]
        set
        {
            <Foo>k__BackingField = value;
        }
    }

    public void Bar([Nullable] string baz, string buz)
    {
    }
}

I noticed two important things:

  1. The compiler generates NullableAttributes on the properties, fields and parameters. This indicates that nullability is explicit and non-nullability is implicit (no attribute).
  2. There is a NonNullTypesAttribute on the module, which enables Roslyn’s nullability analyzer for the whole module (assembly). This attribute can also be used to disable the analyzer for some parts or enable it just for a class or other regions. The NonNullTypesAttribute is used to enable Roslyn’s nullability flow analysis which will produce warnings in Visual Studio and also specify the rules whether something is nullable or not.

The problem: The meaning of not having a NullableAttribute changes depending on the context (i.e. depending whether a NonNullTypesAttribute is active). And determining the current context is probably not trivial. This makes it very hard to do reflection just based on the existence of the NullableAttribute. Wouldn’t it be better to have a NotNullableAttribute instead so that “old” libraries are “nullable” by default and no context is needed when reflecting? This would greatly improve backward compatibility. But maybe this design has been chosen for performance reasons or because in the long run a NullableAttribute makes more sense (e.g. when NonNullTypesAttribute is enabled by default)…

If it’s staying as is, I’m wondering if there will also be a new reflection API to easily reflect over the nullability of properties, parameters and fields? If so, this API should come in an independent, widely usable package so that library authors can use it everywhere, also in “older” libraries…

If you know the answer to one of these questions please write a comment below…

Background: I’m writing libraries to generate JSON Schema (NJsonSchema) and OpenAPI/Swagger specs (NSwag). These libraries rely heavily on reflection. I looked into the Nullable Reference Types feature because I’d really like to use it in these generators…


Tags: , , ,

2 responses to “C# 8: The output of Nullable Reference Types and how to reflect it”

  1. Julien Couvreur says:

    The problem is that “old” libraries are not “nullable”. They are “oblivious”/unknown.
    Without such a third state, you would have problems with using such libraries in a new project: string s = Legacy(); or Legacy("string").

    That said, your point about reflection and the need for assembling two pieces of information (NonNullTypes context + flags from Nullable attribute) stands. This may change.

    • Rico Suter says:

      The problem is that “old” libraries are not “nullable”. They are “oblivious”/unknown.

      From a correctness perspective that is true – nullability is unknown… But from an API declaration perspective everything is implicitely nullable – tools which want to read the nullability have to assume that and then consider additional attributes like NotNullAttribute (R#) or RequiredAttribute. So from a tooling perspective, it would be much simpler to just work with NotNullableAttributes.

Leave a Reply

Your email address will not be published. Required fields are marked *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

This site uses Akismet to reduce spam. Learn how your comment data is processed.