Skip to main content

Anonymous & Nullable Types

Anonymous Types

Definition

Anonymous types are unnamed types created inline using the new keyword with object initializer syntax. The compiler generates a class with read-only properties and infers their types from the assigned values. They are commonly used in LINQ projections and short-lived data shaping.

Creating Anonymous Types

// The compiler creates a class with Name (string) and Age (int) properties
var person = new { Name = "John", Age = 32 };

// Access properties like any other object
Console.WriteLine($"{person.Name}, {person.Age}"); // John, 32

The var Keyword

Anonymous types have no known name at compile time, so you must use var to hold them. The var keyword tells the compiler to infer the type from the initialization expression:

var number = 15;      // int
var word = "example"; // string
var money = 987.32; // double

Key Characteristics

  • Read-only properties — values cannot be changed after creation
  • Compiler-generated — the compiler creates a hidden class with Equals, GetHashCode, and ToString
  • Type inference — property types are inferred from values
  • Scope-limited — primarily used within a method; cannot be passed as return types (use tuples or records instead)
var p1 = new { Name = "Alice", Age = 30 };
var p2 = new { Name = "Alice", Age = 30 };

// Two anonymous objects with the same property names, types, and values are equal
Console.WriteLine(p1.Equals(p2)); // True

// But they are not the same reference
Console.WriteLine(ReferenceEquals(p1, p2)); // False

Common Use: LINQ Projections

var results = products
.Select(p => new { p.Name, p.Price })
.Where(x => x.Price > 100);

foreach (var item in results)
{
Console.WriteLine($"{item.Name}: ${item.Price}");
}
Prefer tuples for method returns

Anonymous types cannot cross method boundaries as return types. If you need to return a shaped result from a method, use tuples (string Name, decimal Price) or record types instead.


Nullable Types

Definition

Nullable types represent values that may also be null. C# supports two kinds: nullable value types (int?, bool?) which wrap System.Nullable<T>, and nullable reference types (C# 8+), a compile-time feature that annotates whether a reference type can be null. Together they form C#'s approach to null safety.

Nullable Value Types

A nullable value type T? can hold all values of its underlying type T plus null. The compiler translates T? into System.Nullable<T>:

int? age = null;
int? score = 85;

// Behind the scenes: Nullable<int>
Console.WriteLine(age.HasValue); // False
Console.WriteLine(score.HasValue); // True
Console.WriteLine(score.Value); // 85

HasValue and Value Properties

int? count = GetCount();

if (count.HasValue)
{
int value = count.Value; // Access the underlying int
Console.WriteLine($"Count: {value}");
}

// Accessing .Value when HasValue is false throws InvalidOperationException

Nullable to Non-Nullable Conversion

int? maybe = 42;

// .Value — throws if null
int a = maybe.Value;

// .GetValueOrDefault() — returns default (0) if null
int b = maybe.GetValueOrDefault();

// .GetValueOrDefault(fallback) — returns fallback if null
int c = maybe.GetValueOrDefault(-1);

// Null-coalescing operator — returns right side if left is null
int d = maybe ?? 0;

// GetValueOrDefault() is the safest approach when you want a default

Nullable Arithmetic

Any arithmetic or comparison operation involving null produces null:

int? x = 10, y = null;

int? sum = x + y; // null
int? product = x * y; // null
bool? equal = x == y; // null (three-valued logic)

// Comparisons with null are false (except !=)
Console.WriteLine(x > y); // false
Console.WriteLine(x == y); // false
Console.WriteLine(x != y); // true

Nullable Reference Types (C# 8+)

Nullable reference types are a compile-time analysis feature — not a runtime change. When enabled, the compiler warns when a reference that could be null is dereferenced:

#nullable enable

string name = "Alice"; // Non-nullable — cannot be null
string? nickname = null; // Nullable — can be null

// Compiler warning CS8602: possible null reference
Console.WriteLine(nickname.Length);

// Safe access with null-conditional
Console.WriteLine(nickname?.Length); // null (no warning)
Console.WriteLine(nickname?.Length ?? 0); // 0

How Nullable Reference Types Work

The compiler performs null state analysis on every reference variable:

StateMeaning
Not nullDefinitely assigned a non-null value
Maybe nullCould be null at this point
#nullable enable

void Process(string? input)
{
// input is "maybe null" here
if (input is null)
return;

// input is "not null" here — compiler knows
Console.WriteLine(input.Length); // No warning
}
string? name = GetName();

// ?. — null-conditional operator: returns null if operand is null
int? length = name?.Length;

// ?? — null-coalescing operator: returns right side if left is null
int safeLength = name?.Length ?? 0;

// ??= — null-coalescing assignment: assigns only if left is null
name ??= "Unknown";

// ! — null-forgiving operator: tells compiler "I know this isn't null"
// Does NOT do a runtime check — only suppresses the warning
int forceLength = name!.Length;
The ! operator does not check for null

The null-forgiving operator (!) only suppresses compiler warnings. It does not add a runtime null check. If the value is actually null, you will still get a NullReferenceException at runtime.

Enabling Nullable Reference Types

<!-- Project level — .csproj -->
<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>
// File level
#nullable enable

// Disable for a section
#nullable disable
string legacy = null; // No warning
#nullable enable

// Restore to project setting
#nullable restore

Nullable Attributes

The compiler uses attributes to understand null state across method boundaries:

// Return value is not null when the method returns true
bool TryGetValue([NotNullWhen(true)] out string? value);

// Parameter may be null when the method returns false
[return: MaybeNullWhen(false)]
T Find<T>(Predicate<T> match);

// Parameter is not null after the method returns
void EnsureCapacity([NotNull] ref string? value);

// Parameter will not be null if the return value is not null
[return: NotNullIfNotNull(nameof(input))]
string? Transform(string? input);
AttributeMeaning
[NotNullWhen(true)]Parameter is not null when method returns true
[NotNullWhen(false)]Parameter is not null when method returns false
[MaybeNullWhen(true)]Parameter may be null when method returns true
[NotNull]Output is guaranteed not null
[NotNullIfNotNull]Return is not null if the specified parameter is not null
[MemberNotNullWhen]Named member is not null when method returns the value

When to Use

  • Database values — map nullable columns to nullable types (int?, DateTime?)
  • API boundaries — nullable reference types enforce null contracts at compile time
  • Optional parameters — use T? when null means "not provided"
  • Legacy migration — enable NRTs incrementally with #nullable enable per file

Common Pitfalls

Disabling warnings instead of fixing

Do not use ! or #nullable disable to silence warnings without understanding the null state. Each suppressed warning is a potential NullReferenceException at runtime. Fix the root cause: add null checks, provide defaults, or change the type.

Mixing nullable and non-nullable in APIs

If your public API accepts string but your implementation can produce null, callers will not expect null. Make the signature honest: use string? if null is possible. Nullable reference types make these contracts explicit.

Migrate incrementally

For large codebases, enable nullable reference types one file or project at a time. Use #nullable enable at the top of files you want to migrate and fix warnings before expanding to more files.

Key Takeaways

  1. Anonymous types are compiler-generated, read-only classes created inline with new { ... }.
  2. Use var to hold anonymous type instances since the type name is unknown at compile time.
  3. T? on value types creates Nullable<T> with .HasValue and .Value.
  4. T? on reference types is a compile-time annotation — no runtime change.
  5. The null-coalescing operator ?? and null-conditional operator ?. are the primary tools for null-safe code.
  6. Nullable reference types with #nullable enable catch null bugs at compile time.
  7. The ! operator suppresses warnings but does not add runtime checks — use sparingly.
  8. Nullable attributes ([NotNullWhen], etc.) help the compiler understand method contracts.

Interview Questions

Q: What is an anonymous type in C#? An anonymous type is a compiler-generated class with no explicit name, created using new { Property = value } syntax. Properties are read-only and their types are inferred from the assigned values. You must use var to hold instances. They are commonly used in LINQ projections for shaping data.

Q: Can you return an anonymous type from a method? No, anonymous types cannot be used as method return types because their type name is compiler-generated and inaccessible in code. Use tuples (string Name, int Age) or record types instead when you need to return shaped data from a method.

Q: What is a nullable value type? A nullable value type T? is a System.Nullable<T> struct that can hold all values of T plus null. It exposes .HasValue (bool) and .Value (T) properties. It is useful for representing optional or database values.

Q: What are nullable reference types? Nullable reference types (C# 8+) are a compile-time analysis feature. When enabled, string? means "this reference can be null" and string means "this reference should not be null." The compiler warns when a nullable reference is dereferenced without a null check. This is not a runtime change — it only produces warnings.

Q: What is the difference between int and int?? int is a value type stored on the stack that cannot be null. int? is Nullable<int>, a struct that wraps an int with a HasValue flag, allowing it to represent null. int? has a larger memory footprint and requires .Value or .GetValueOrDefault() to access the underlying value.

Q: What does the ?. operator do? The null-conditional operator (?.) short-circuits member access if the operand is null. If obj is null, obj?.Property returns null instead of throwing NullReferenceException. The return type is always nullable. Chain it with ?? to provide a default: obj?.Property ?? fallback.

Q: What is the difference between nullable value types and nullable reference types? Nullable value types (int?) are a runtime feature — they wrap the value in Nullable<T> with extra state. Nullable reference types (string?) are a compile-time-only feature — the compiler analyzes null states and produces warnings, but the runtime representation is the same as a normal reference.

References