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
211: Nullable Value Types (Nullable<T>) In Depth
I remember a junior dev I worked with a few years ago who spent an entire afternoon chasing a ghost in a reporting tool. He was pulling a "Last Login Day" value from a SQL database. Some users had never logged in, so the database column was NULL. In C#, he mapped this field to a standard int. Because a value type like int cannot be null, the framework assigned it the default value of 0. Suddenly, users who had never logged in were being reported as having logged in on January 1, 1900. It looked like massive data corruption, but it was actually just a fundamental misunderstanding of how value types behave when they encounter a void.
In C#, we have a strict divide between reference types (which can be null) and value types (which cannot). An int, a bool, or a DateTime always holds a value. This is great for performance and memory predictability, but it's a nightmare when you're dealing with optional data—like a middle name that might be missing or a sensor reading that failed to report.
The Gap Between Value Types and Databases
To solve this, C# gives us Nullable<T>. You've likely seen the shorthand version, like int? or bool?. It's important to realize that int? is just syntactic sugar for Nullable<int>. When you use this, you aren't actually changing the int into a reference type; instead, you're wrapping that int inside a special structure that can track whether a value is actually present.
int? optionalAge = null;
bool? isSubscriptionActive = null;
DateTime? lastLogin = null;
if (optionalAge == null)
{
Console.WriteLine("Age was not provided.");
}
I've seen people try to treat these like regular integers immediately, which leads to compiler errors. You can't pass an int? into a method that expects a regular int without explicitly handling the null case first. The compiler is protecting you from the exact bug my colleague encountered years ago.
Peeking Under the Hood of Nullable<T>
Since Nullable<T> is a struct, it has two primary properties you need to know: HasValue and Value. The HasValue property is a boolean that tells you if the variable is actually holding data. The Value property gives you the underlying value of type T.
Here is the danger zone: if you try to access .Value when HasValue is false, C# will throw an InvalidOperationException. It doesn't return a default; it crashes. I always tell my students to treat .Value like a loaded gun—don't pull the trigger unless you've checked the safety first.
int? score = null;
// This will crash the program!
// int finalScore = score.Value;
if (score.HasValue)
{
int finalScore = score.Value;
Console.WriteLine($"Your score is: {finalScore}");
}
Handling the Absence of Value Safely
Checking HasValue everywhere makes your code look cluttered and "stuttery." In a professional codebase, we rarely do that. Instead, we use the null-coalescing operator (??). This operator allows you to provide a fallback value in a single, readable line. If the left side is null, it takes the right side.
int? sensorReading = null;
// If sensorReading is null, use -1 as a sentinel value
int cleanReading = sensorReading ?? -1;
Console.WriteLine($"Reading: {cleanReading}"); // Output: Reading: -1
You can also use the null-conditional operator (?.) if you're dealing with nullable types that have methods or properties, though this is more common with reference types. For value types, the combination of ?? and GetValueOrDefault() are your primary tools for keeping your logic clean and your application stable.
📋 Practical Task
Exercise: Repairing the Weather Station Data Processor
You have been handed a piece of code for a weather station that is incorrectly reporting temperatures. The system currently uses a standard double for temperature, meaning missing readings (which should be null) are being treated as 0.0 degrees. This is skewing the average temperature calculations during winter months.
Your Task:
- Modify the
WeatherReadingclass to use a nullabledouble?for theTemperatureproperty. - Update the
CalculateAveragemethod so that it ignores readings where the temperature isnull, rather than counting them as0.0. - Use the null-coalescing operator (
??) to print "N/A" or the temperature value when displaying the logs.
using System;
using System.Collections.Generic;
using System.Linq;
public class WeatherReading
{
public DateTime Timestamp { get; set; }
public double Temperature { get; set; } // TODO: Make this nullable
}
public class WeatherProcessor
{
public static void Main()
{
var readings = new List<WeatherReading>
{
new WeatherReading { Timestamp = DateTime.Now, Temperature = 22.5 },
new WeatherReading { Timestamp = DateTime.Now.AddHours(-1), Temperature = 0.0 }, // Is this 0 degrees or missing?
new WeatherReading { Timestamp = DateTime.Now.AddHours(-2), Temperature = 19.8 }
};
// TODO: Fix the average calculation to ignore nulls
double average = readings.Average(r => r.Temperature);
Console.WriteLine($"Average Temp: {average}");
}
}
There are no comments for now.