C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
41: Nullable Types and Null Safety
You've probably seen it. Maybe it was your first day on the job, or maybe it was last week. The NullReferenceException. It's the most infamous error in C# because it usually happens in the one edge case you forgot to account for. Let's look at a piece of code that looks perfectly fine at first glance but is actually a ticking time bomb.
public class UserProfile
{
public string FirstName { get; set; }
public string MiddleName { get; set; } // Some users don't have one
public string LastName { get; set; }
}
public class UserDisplay
{
public string GetFullName(UserProfile profile)
{
// This looks clean, right?
return $"{profile.FirstName} {profile.MiddleName.ToUpper()} {profile.LastName}";
}
}
If you run this with a user who has a middle name, it works great. But the second you pass in a profile where MiddleName is null, the app crashes. Why? Because you're trying to call .ToUpper() on a reference that points to nothing. You can't perform an operation on a void.
Stopping the Crash with Null-Conditional Operators
In the old days, we'd wrap everything in if (profile.MiddleName != null) blocks. It worked, but it made the code bloated and hard to read. Now, we have the null-conditional operator (?.). This operator tells C#: "If the thing on the left is null, stop right here and just return null for the whole expression. Don't try to call the method."
However, if we just use profile.MiddleName?.ToUpper(), our return type becomes a nullable string, and we might end up with a weird "null" string printed in our UI. That's where the null-coalescing operator (??) comes in. It lets us provide a fallback value.
public string GetFullName(UserProfile profile)
{
// If MiddleName is null, the ?. stops the chain.
// If the result of that chain is null, the ?? kicks in and uses an empty string.
string middle = profile.MiddleName?.ToUpper() ?? "";
return $"{profile.FirstName} {middle} {profile.LastName}".Replace(" ", " ").Trim();
}
Defining Intent with Nullable Reference Types
The real problem with the first example wasn't just the crash—it was that the UserProfile class lied to us. It claimed MiddleName was a string, implying it should always have a value. In modern C#, we use Nullable Reference Types (NRTs) to be honest about our data.
By adding a ? to the type, you're telling the compiler (and other developers), "Heads up: this value might actually be null."
public class UserProfile
{
public string FirstName { get; set; } = string.Empty;
public string? MiddleName { get; set; } // Explicitly nullable
public string LastName { get; set; } = string.Empty;
}
Once you enable Nullable Reference Types in your project settings, the compiler stops being a passive observer. If you try to call profile.MiddleName.ToUpper() without a null check, you'll get a warning in your IDE. I actually love these warnings; they're essentially the compiler saying, "I've seen this bug a thousand times, and you're about to make it again."
Handling Value Types with Nullable
One quick distinction: string is a reference type, but int or bool are value types. Value types can't be null by default—an int is always at least 0. But what if you're dealing with a database where a "Age" column can be empty? You can't use 0, because 0 is a valid age for a newborn.
For these, we use int? (which is shorthand for Nullable<int>). These wrappers have a HasValue property and a Value property.
int? userAge = null;
if (userAge.HasValue)
{
Console.WriteLine($"Age is {userAge.Value}");
}
else
{
Console.WriteLine("Age not provided");
}
My advice? Be aggressive with ?. If a piece of data isn't 100% guaranteed to exist, mark it as nullable. It forces you to handle the edge cases while you're writing the code, rather than discovering them via a crash report in production.
📋 Practical Task
Fixing the Product Inventory Null Guard
You are working on an e-commerce system. There is a Product class where the DiscountPrice is optional (nullable), and the Description might be null if the vendor didn't provide one. The current code is crashing when products are missing this information.
Your Task: Modify the GetProductSummary method to prevent NullReferenceExceptions. Use the null-conditional operator (?.) and the null-coalescing operator (??) to ensure that:
- If
Descriptionis null, it displays "No description available". - If
DiscountPriceis null, it displays "Full Price". Otherwise, it displays the price formatted as currency.
public class Product
{
public string Name { get; set; } = "Unknown Product";
public string? Description { get; set; }
public decimal? DiscountPrice { get; set; }
}
public class ProductService
{
public string GetProductSummary(Product product)
{
// TODO: Fix the following lines to prevent crashes and handle nulls
string desc = product.Description.Trim();
string price = product.DiscountPrice.Value.ToString("C");
return $"Product: {product.Name} | Description: {desc} | Price: {price}";
}
}There are no comments for now.