<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/internals-of-c-nullable-reference-types---migrating-to-nullable-reference-types---part-2-ayjpvmckr" -->

---
title: Internals of C# nullable reference types - Migrating to...
description: This deep-dive explains how the C# compiler encodes nullable reference type (NRT) information into Intermediate Language via NullableContextAttribute and...
canonical: https://daily.dev/posts/internals-of-c-nullable-reference-types---migrating-to-nullable-reference-types---part-2-ayjpvmckr
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Internals of C# nullable reference types - Migrating to nullable reference types - Part 2 | daily.dev
og:description: This deep-dive explains how the C# compiler encodes nullable reference type (NRT) information into Intermediate Language via NullableContextAttribute and...
og:url: https://daily.dev/posts/internals-of-c-nullable-reference-types---migrating-to-nullable-reference-types---part-2-ayjpvmckr
og:image: https://api.daily.dev/og/posts/ayjPvmCkr.png
og:image:alt: Internals of C# nullable reference types - Migrating to nullable reference types - Part 2
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Internals of C# nullable reference types - Migrating to nullable reference types - Part 2

**[Maarten Balliauw](https://daily.dev/sources/maartenballiauw)** · 10 min read · 0 upvotes · 0 comments

## Summary

This deep-dive explains how the C# compiler encodes nullable reference type (NRT) information into Intermediate Language via NullableContextAttribute and NullableAttribute, with disassembled IL examples showing how value 0/1/2 signal oblivious, not-annotated, or annotated contexts. It then covers the four nullable annotation context settings (disable, enable, warnings, annotations), how to configure them project-wide via the csproj Nullable property or per-file with #nullable directives, and discusses trade-offs of each setting as a starting point for migrating an existing codebase to NRT, concluding that warnings is a good gradual starting point while annotations alone is of limited use.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://blog.maartenballiauw.be/posts/2022-04-19-internals-of-csharp-nullable-reference-types-migrating-to-nullable-reference-types-part-2>

## Questions this post answers

### How does the C# compiler represent nullable reference types in the compiled IL?

The compiler emits NullableContextAttribute and NullableAttribute custom attributes with an 8-bit integer value: 0 means oblivious (pre-C#8 behavior), 1 means not annotated (non-nullable by default), and 2 means annotated (nullable by default, as if every reference type had a trailing ?). These attributes are applied at the method, parameter, or type level, and the compiler tries to emit as few of them as possible by setting a default context and only flagging exceptions.

_Developers decoding IL metadata for nullable types can compare notes on daily.dev._

### What is the difference between the enable, disable, warnings, and annotations nullable contexts in a C# project?

disable makes the compiler behave like pre-C# 8.0 with no nullable reference type support and disallows using ? on reference types. enable turns on both null reference flow analysis and the ability to use ? annotations. warnings turns on flow analysis and shows warnings for potential null dereferences without enabling annotation syntax. annotations lets you use ? syntax but disables flow analysis warnings entirely.

_Teams planning a nullable reference types migration can track C# tooling guidance on daily.dev._

### What is a good strategy for migrating an existing large C# codebase to nullable reference types without being overwhelmed by warnings?

Start with disable as the project default (or omit the Nullable property entirely) and incrementally add #nullable enable file by file as each file is migrated, avoiding a flood of warnings all at once. Alternatively, start with warnings as the default to get flow-analysis warnings on potential null dereferences without yet requiring annotation syntax, then promote files to enable once fixed, optionally setting WarningsAsErrors to Nullable to enforce addressing them.

_Developers phasing in nullable reference types across legacy code can follow migration patterns like this on daily.dev._

## Similar posts on daily.dev

- [Taming the Billion Dollar Mistake: Maarten Balliauw's Guide to C\# Nullable Reference Types](https://daily.dev/posts/taming-the-billion-dollar-mistake-maarten-balliauw-s-guide-to-c-nullable-reference-types-dhuc5b5o7) · JetBrains · 5 upvotes · 0 comments
- [What Actually Changed Inside the .NET 11 JIT Compiler — Daily DevOps & .NET](https://daily.dev/posts/what-actually-changed-inside-the-net-11-jit-compiler-daily-devops-net-s6qx7oe7d) · Daily DevOps & .NET · 27 upvotes · 4 comments
- [Making Reflection Native AOT Safe in .NET 10: DynamicallyAccessedMembers Guide](https://daily.dev/posts/making-reflection-native-aot-safe-in-net-10-dynamicallyaccessedmembers-guide-xpy12bjep) · We Are .NET · 3 upvotes · 0 comments

---

Tags: [#c#](https://daily.dev/tags/c#)

[View this post on daily.dev](https://daily.dev/posts/internals-of-c-nullable-reference-types---migrating-to-nullable-reference-types---part-2-ayjpvmckr)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Internals of C# nullable reference types - Migrating to nullable reference types - Part 2","url":"https://daily.dev/posts/internals-of-c-nullable-reference-types---migrating-to-nullable-reference-types---part-2-ayjpvmckr","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/internals-of-c-nullable-reference-types---migrating-to-nullable-reference-types---part-2-ayjpvmckr"},"datePublished":"2026-10-04T13:04:12.097Z","dateModified":"2026-10-04T13:05:26.709Z","description":"This deep-dive explains how the C# compiler encodes nullable reference type (NRT) information into Intermediate Language via NullableContextAttribute and...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/d82e6ea50425e2c3d1c29bafb291ffbc?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/d82e6ea50425e2c3d1c29bafb291ffbc?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Maarten Balliauw","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Maarten Balliauw","logo":"https://media.daily.dev/image/upload/s--rOTg3VYO--/f_auto,q_auto/v1791119043/logos/maartenballiauw?_a=BAMAMicg0","url":"https://daily.dev/sources/maartenballiauw"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/internals-of-c-nullable-reference-types---migrating-to-nullable-reference-types---part-2-ayjpvmckr","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"c#","timeRequired":"PT10M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Maarten Balliauw","item":"https://daily.dev/sources/maartenballiauw"},{"@type":"ListItem","position":3,"name":"Internals of C# nullable reference types - Migrating to nullable reference types - Part 2"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/internals-of-c-nullable-reference-types---migrating-to-nullable-reference-types---part-2-ayjpvmckr#faq","mainEntity":[{"@type":"Question","name":"How does the C# compiler represent nullable reference types in the compiled IL?","acceptedAnswer":{"@type":"Answer","text":"The compiler emits NullableContextAttribute and NullableAttribute custom attributes with an 8-bit integer value: 0 means oblivious (pre-C#8 behavior), 1 means not annotated (non-nullable by default), and 2 means annotated (nullable by default, as if every reference type had a trailing ?). These attributes are applied at the method, parameter, or type level, and the compiler tries to emit as few of them as possible by setting a default context and only flagging exceptions. Developers decoding IL metadata for nullable types can compare notes on daily.dev."}},{"@type":"Question","name":"What is the difference between the enable, disable, warnings, and annotations nullable contexts in a C# project?","acceptedAnswer":{"@type":"Answer","text":"disable makes the compiler behave like pre-C# 8.0 with no nullable reference type support and disallows using ? on reference types. enable turns on both null reference flow analysis and the ability to use ? annotations. warnings turns on flow analysis and shows warnings for potential null dereferences without enabling annotation syntax. annotations lets you use ? syntax but disables flow analysis warnings entirely. Teams planning a nullable reference types migration can track C# tooling guidance on daily.dev."}},{"@type":"Question","name":"What is a good strategy for migrating an existing large C# codebase to nullable reference types without being overwhelmed by warnings?","acceptedAnswer":{"@type":"Answer","text":"Start with disable as the project default (or omit the Nullable property entirely) and incrementally add #nullable enable file by file as each file is migrated, avoiding a flood of warnings all at once. Alternatively, start with warnings as the default to get flow-analysis warnings on potential null dereferences without yet requiring annotation syntax, then promote files to enable once fixed, optionally setting WarningsAsErrors to Nullable to enforce addressing them. Developers phasing in nullable reference types across legacy code can follow migration patterns like this on daily.dev."}}]}
```

