← Back to Articles         
C# nullable reference types, pattern matching

C# nullable reference types, pattern matching

Published on 12 Oct 2026     5 min read C#
Modern C# Features

Modern C# Features – Interview Questions

1. What are Nullable Reference Types?

Nullable Reference Types (C# 8.0) help identify possible null reference errors during compilation.

By default, when nullable reference types are enabled, a reference type is considered non-nullable unless declared with ?.

Example

#nullable enable

string name = "John";     // Non-nullable
string? city = null;      // Nullable

Console.WriteLine(name);
Console.WriteLine(city?.Length);

Explanation:

  • string indicates that the variable should not contain null.

  • string? explicitly allows null.

  • ?. safely accesses a member when the object is not null.

  • The compiler generates warnings for possible null-reference problems.

Key Points

  • Introduced in C# 8.0.

  • Helps prevent NullReferenceException.

  • Provides compile-time warnings, not runtime enforcement.

  • Enable using <Nullable>enable</Nullable> in .csproj.

 

2. What is Pattern Matching in C#?

Pattern matching checks whether a value matches a particular type, constant, range, or structure.

It reduces complex if-else conditions.

Example

int age = 25;

if (age is >= 18 and <= 60)
{
    Console.WriteLine("Eligible");
}
else
{
    Console.WriteLine("Not Eligible");
}

Output:

Eligible

Explanation:

  • is performs pattern matching.

  • >= 18 checks the minimum age.

  • and combines two conditions.

  • <= 60 checks the maximum age.

Key Points

  • Supports type, constant, relational, property, and other patterns.

  • Uses keywords such as is, and, or, and not.

  • Reduces lengthy conditional statements.

  • Useful for validation and business rules.

 

3. What are Switch Expressions?

A switch expression (C# 8.0) is a shorter way to evaluate multiple conditions and return a value.

Unlike a traditional switch statement, it directly produces a result.

Example

int status = 2;

string result = status switch
{
    1 => "Pending",
    2 => "Approved",
    3 => "Rejected",
    _ => "Unknown"
};

Console.WriteLine(result);

Output:

Approved

Explanation:

  • status switch evaluates the value.

  • => specifies the result for each matching pattern.

  • _ represents the default case.

  • No break statement is required.

Key Points

  • Introduced in C# 8.0.

  • More concise than traditional switch statements.

  • Returns a value directly.

  • Supports pattern matching.

  • Useful for status mapping and business rules.

 

4. What Are init-Only Properties?

init-only properties (C# 9.0) allow property values to be assigned during object initialization but not modified afterward.

Example

public class Employee
{
    public int Id { get; init; }
    public string Name { get; init; } = "";
}

// Object initialization
Employee emp = new Employee
{
    Id = 101,
    Name = "John"
};

Console.WriteLine(emp.Name);

// emp.Name = "David"; // Compile-time error

Explanation:

  • init allows setting values during initialization.

  • After initialization, reassignment is not allowed.

  • Helps create objects with stable property values.

Key Points

  • Introduced in C# 9.0.

  • Supports initialization-time immutability.

  • Commonly used with records and DTOs.

  • Unlike set, init prevents later property reassignment.

  • Does not make referenced objects deeply immutable.

 

5. What Is the required Keyword?

The required keyword (C# 11) ensures that a property or field must be initialized when creating an object, unless a constructor explicitly satisfies the requirement.

Example

public class Employee
{
    public required string Name { get; init; }
    public int Age { get; set; }
}

Employee emp = new Employee
{
    Name = "John"
};

Console.WriteLine(emp.Name);

Explanation:

  • Name must be initialized.

  • Age is optional.

  • If Name is not initialized, the compiler reports an error.

Employee emp = new Employee();
// Compile-time error: Name is required

Key Points

  • Introduced in C# 11.

  • Enforces member initialization at compile time.

  • Works with both set and init.

  • Useful for DTOs, configuration objects, and domain models.

  • Does not automatically prevent assigning null at runtime.

 

6. What Are Primary Constructors?

Primary constructors (C# 12) allow constructor parameters to be declared directly in the class or struct declaration.

They reduce constructor boilerplate.

Example

public class Employee(int id, string name)
{
    public void Display()
    {
        Console.WriteLine($"{id} - {name}");
    }
}

// Usage
Employee emp = new Employee(101, "John");
emp.Display();

Output:

101 - John

Explanation:

  • id and name are primary constructor parameters.

  • Values are passed while creating the object.

  • Parameters can be accessed inside instance members.

  • No separate constructor declaration is required.

Key Points

  • Introduced for classes and structs in C# 12.

  • Reduces boilerplate code.

  • Useful for Dependency Injection.

  • Primary constructor parameters are not automatically public properties.

  • Records also support primary constructor syntax, with different property-generation behavior.

ASP.NET Core Example:

public class EmployeeService(
    ILogger<EmployeeService> logger)
{
    public void Process()
    {
        logger.LogInformation("Processing employee");
    }
}

This is useful for constructor-based Dependency Injection.

 

7. What Are Collection Expressions?

Collection expressions (C# 12) provide a simpler syntax for creating arrays, lists, spans, and other supported collection types.

They use square brackets [].

Example

// Traditional syntax
int[] numbers1 = new int[] { 10, 20, 30 };

// Collection expression
int[] numbers2 = [10, 20, 30];

// List
List<string> names = ["John", "David", "Peter"];

foreach (string name in names)
{
    Console.WriteLine(name);
}

Output:

John
David
Peter

Explanation:

  • [10, 20, 30] creates an array using the declared target type.

  • ["John", "David", "Peter"] initializes a List<string>.

  • Collection expressions reduce unnecessary syntax.

Key Points

  • Introduced in C# 12.

  • Uses [] syntax.

  • Supports arrays, lists, spans, and compatible collection types.

  • Supports spread elements using ...

Example:

int[] first = [1, 2];
int[] second = [3, 4];

int[] combined = [..first, ..second];

// Result: 1, 2, 3, 4
 

Quick Interview Revision

Feature

C# Version

Main Purpose

Nullable Reference Types

C# 8

Reduce null-reference errors

Pattern Matching

C# 7 onward

Simplify conditional checks

Switch Expressions

C# 8

Return values using concise switch syntax

init Properties

C# 9

Prevent reassignment after initialization

required Keyword

C# 11

Enforce initialization

Primary Constructors

C# 12

Simplify constructor declarations

Collection Expressions

C# 12

Simplify collection initialization

Important interview differences:

  • set vs init: set allows modification later; init allows assignment only during initialization.

  • required vs init: required enforces initialization; init restricts when assignment can occur.

  • Traditional switch vs switch expression: Traditional switch executes statements; a switch expression produces a value.

  • Normal constructor vs primary constructor: A primary constructor reduces boilerplate by placing parameters in the type declaration.

← Back to Articles