C# 11.0 was released in November 2022 alongside .NET 7. It introduced improvements in object initialization, string handling, generic programming, pattern matching, and UTF-8 processing.
The five important features are:
-
Required Members (
required) – Enforces initialization of mandatory properties. -
Raw String Literals – Simplifies multiline strings, JSON, SQL, and text containing special characters.
-
Generic Math – Supports mathematical operations on generic numeric types.
-
List Patterns – Enables pattern matching on arrays and lists.
-
UTF-8 String Literals – Creates UTF-8 encoded byte data directly from string literals.
1. Required Members (required)
What is it?
The required keyword was introduced in C# 11 to ensure that specific fields or properties are initialized when creating an object.
Before C# 11, developers commonly used constructors to enforce mandatory values.
With required, we can declare mandatory properties and initialize them using object initializers.
Why is it used?
Consider an Employee class with three properties:
-
Id– Mandatory -
Name– Mandatory -
Department– Optional
We want to ensure that every employee has an ID and name.
Example Program
public class Employee
{
public required int Id { get; set; }
public required string Name { get; set; }
public string? Department { get; set; }
}
class Program
{
static void Main()
{
Employee emp = new Employee
{
Id = 101,
Name = "John",
Department = "IT"
};
Console.WriteLine(emp.Id);
Console.WriteLine(emp.Name);
Console.WriteLine(emp.Department);
}
}
Output
101
John
IT
How does it work?
Employee Class
|
|-- required Id
|-- required Name
|-- optional Department
|
v
Create Employee Object
|
v
Compiler checks required members
|
+-- All assigned --> Compilation succeeds
|
+-- Missing one --> Compilation error
What happens if a required property is missing?
Employee emp = new Employee
{
Id = 101
};
Result: Compilation error because the Name property was not initialized.
Difference between required and init
|
|
|
|---|---|
|
Enforces initialization |
Restricts when assignment is allowed |
|
Can use |
Uses |
|
Property must be assigned during object creation |
Property initialization is optional unless also marked |
|
Does not prevent later changes |
Prevents normal assignment after initialization |
We can combine both:
public required string Name { get; init; }
This ensures the caller supplies Name during object creation and prevents normal reassignment afterward.
Key Points
-
Introduced in C# 11.
-
Applicable to fields and properties.
-
Enforced by the compiler, not automatically by runtime validation.
-
A
requiredreference property can still receivenullin some circumstances; nullable reference type analysis helps detect that. -
Constructors marked with
[SetsRequiredMembers]can indicate that they initialize required members. -
Useful for DTOs, configuration objects, and domain models.
Interview Tip: required enforces initialization, whereas init restricts modification after initialization. They solve different problems.
2. Raw String Literals
What is it?
Raw String Literals were introduced in C# 11 to simplify writing strings containing special characters, double quotes, and multiple lines.
They use three or more double quotes (""") as delimiters.
Unlike normal strings, raw strings do not require escaping most special characters.
Why is it used?
Before C# 11, writing JSON or multiline SQL queries could require escape characters or verbatim strings.
For example:
string json = "{\"name\":\"John\",\"age\":30}";
With C# 11:
string json = """
{
"name": "John",
"age": 30
}
""";
The second version is easier to read and maintain.
Example Program
class Program
{
static void Main()
{
string json = """
{
"id": 101,
"name": "John",
"department": "IT"
}
""";
Console.WriteLine(json);
}
}
Output
{
"id": 101,
"name": "John",
"department": "IT"
}
How does it work?
Raw String Literal
|
v
Opening delimiter (""")
|
v
String Content
- Quotes allowed
- Backslashes preserved
- Multiple lines supported
|
v
Closing delimiter (""")
|
v
String created without escape processing
Example: SQL Query
string sql = """
SELECT
EmployeeId,
EmployeeName,
Department
FROM Employees
WHERE Department = 'IT'
ORDER BY EmployeeName
""";
Console.WriteLine(sql);
This is particularly useful when working with SQL Server queries in .NET applications.
Raw String with Interpolation
Raw strings also support interpolation.
string name = "John";
string message = $"""
Hello {name},
Welcome to C# 11.
""";
Console.WriteLine(message);
Output
Hello John,
Welcome to C# 11.
Normal String vs Verbatim String vs Raw String
|
Feature |
Normal |
Verbatim ( |
Raw ( |
|---|---|---|---|
|
Multiline support |
With escapes |
Yes |
Yes |
|
Backslash escaping |
Required |
Not required |
Not required |
|
Double quotes |
|
|
Usually no escaping |
|
Best for |
Short text |
Paths, multiline text |
JSON, SQL, XML |
Key Points
-
Introduced in C# 11.
-
Uses at least three double quotes.
-
Supports multiline content.
-
Preserves backslashes without escape processing.
-
Supports string interpolation.
-
More quotes can be used as delimiters when the content itself contains triple quotes.
-
Useful for JSON, XML, SQL, regular expressions, and templates.
Interview Tip: Raw String Literals improve readability by eliminating unnecessary escaping, especially when embedding JSON and SQL.
3. Generic Math
What is it?
Generic Math was introduced in C# 11 to perform mathematical operations on generic numeric types.
Previously, mathematical operators such as +, -, *, and / could not generally be applied to unconstrained generic type parameters.
C# 11 introduced support for static abstract interface members, enabling generic numeric interfaces such as INumber<T>.
Why is it used?
Suppose we want to create a method that adds two numbers.
Without Generic Math, we might create separate methods:
int Add(int a, int b)
{
return a + b;
}
double Add(double a, double b)
{
return a + b;
}
This duplicates logic.
With Generic Math, we can write one generic method for supported numeric types.
Example Program
using System.Numerics;
class Program
{
static T Add<T>(T a, T b)
where T : INumber<T>
{
return a + b;
}
static void Main()
{
Console.WriteLine(Add(10, 20));
Console.WriteLine(Add(10.5, 20.5));
Console.WriteLine(Add(100m, 200m));
}
}
Output
30
31
300
Program Explanation
Step 1: Import System.Numerics
using System.Numerics;
This namespace provides generic numeric interfaces.
Step 2: Define a generic method
static T Add<T>(T a, T b)
where T : INumber<T>
Here:
-
Trepresents a numeric type. -
INumber<T>constrainsTto implement the required numeric operations. -
The
+operator can be used withT.
Step 3: Call the method
Add(10, 20); // int
Add(10.5, 20.5); // double
Add(100m, 200m); // decimal
The compiler infers the appropriate type for each call.
How does it work?
Generic Add<T>()
|
v
Check T : INumber<T>
|
+-- int --------+
| |
+-- double -----+--> Execute a + b
| |
+-- decimal ----+
|
v
Return result as T
Real-World Example: Calculate Total
using System.Numerics;
public static T CalculateTotal<T>(IEnumerable<T> values)
where T : INumber<T>
{
T total = T.Zero;
foreach (T value in values)
{
total += value;
}
return total;
}
Usage:
int[] quantities = { 10, 20, 30 };
decimal[] amounts = { 100.50m, 200.25m };
Console.WriteLine(CalculateTotal(quantities));
Console.WriteLine(CalculateTotal(amounts));
Output
60
300.75
The same method works for integer quantities and decimal amounts.
Important Generic Math Interfaces
|
Interface |
Purpose |
|---|---|
|
|
General numeric operations |
|
|
Addition |
|
|
Subtraction |
|
|
Multiplication |
|
|
Division |
|
|
Fundamental numeric capabilities |
Key Points
-
Introduced in C# 11, with generic math interfaces available in .NET 7.
-
Uses static abstract members in interfaces.
-
Enables arithmetic on appropriately constrained generic types.
-
Reduces duplicate methods for different numeric types.
-
Supports numeric types such as
int,double, anddecimal. -
Useful for mathematical libraries, financial calculations, statistics, and reusable algorithms.
Interview Tip: Generic Math allows strongly typed arithmetic on generic numeric types without relying on dynamic or runtime type checks.
4. List Patterns
What is it?
List Patterns were introduced in C# 11 to match arrays and list-like collections based on their elements and structure.
They allow pattern matching using square brackets ([]).
Why is it used?
Suppose we want to check whether an array:
-
Contains specific values.
-
Starts with a particular value.
-
Ends with a particular value.
-
Contains a particular sequence.
Before C# 11, we generally used multiple conditions.
if (numbers.Length == 3 &&
numbers[0] == 10 &&
numbers[1] == 20 &&
numbers[2] == 30)
{
Console.WriteLine("Matched");
}
With List Patterns:
if (numbers is [10, 20, 30])
{
Console.WriteLine("Matched");
}
Example Program
class Program
{
static void Main()
{
int[] numbers = { 10, 20, 30 };
if (numbers is [10, 20, 30])
{
Console.WriteLine("Exact Match");
}
if (numbers is [10, ..])
{
Console.WriteLine("Starts with 10");
}
if (numbers is [.., 30])
{
Console.WriteLine("Ends with 30");
}
}
}
Output
Exact Match
Starts with 10
Ends with 30
How does it work?
Array: [10, 20, 30]
|
v
Apply List Pattern
|
+-- [10, 20, 30]
| Exact Match
|
+-- [10, ..]
| Starts with 10
|
+-- [.., 30]
Ends with 30
Different List Patterns
|
Pattern |
Meaning |
|---|---|
|
|
Empty collection |
|
|
Exactly one element equal to 10 |
|
|
Exactly two matching elements |
|
|
Starts with 10 |
|
|
Ends with 30 |
|
|
Starts with 10 and ends with 30 |
|
|
Captures the first element |
|
|
Exactly three elements, with 20 in the middle |
Example: Using Switch Expression
int[] numbers = { 10, 20, 30 };
string result = numbers switch
{
[] => "Empty Array",
[10] => "Only 10",
[10, 20] => "Two Elements",
[10, ..] => "Starts with 10",
_ => "Other Pattern"
};
Console.WriteLine(result);
Output
Starts with 10
Key Points
-
Introduced in C# 11.
-
Uses square brackets for pattern matching.
-
Supports exact matching and partial matching.
-
..represents zero or more elements. -
Can be combined with
if,switch, andswitchexpressions. -
Works with arrays,
List<T>, and compatible indexable types. -
List patterns check structure and elements; they do not automatically search every element of a collection.
Interview Tip: List Patterns simplify conditional logic when checking collection structure, such as matching the first or last element.
5. UTF-8 String Literals
What is it?
UTF-8 String Literals were introduced in C# 11 to create UTF-8 encoded byte sequences directly from string literals.
They use the u8 suffix.
Normally, C# strings use UTF-16 encoding internally.
When UTF-8 bytes are required, developers commonly use Encoding.UTF8.GetBytes().
C# 11 provides a simpler alternative for constant text.
Why is it used?
UTF-8 is commonly used in:
-
Web APIs
-
JSON processing
-
Network communication
-
File processing
-
Binary protocols
Before C# 11
using System.Text;
byte[] data = Encoding.UTF8.GetBytes("Hello");
With C# 11
ReadOnlySpan<byte> data = "Hello"u8;
The compiler provides UTF-8 encoded bytes for the literal.
Example Program
class Program
{
static void Main()
{
ReadOnlySpan<byte> data = "Hello"u8;
foreach (byte value in data)
{
Console.WriteLine(value);
}
}
}
Output
72
101
108
108
111
These are the UTF-8 byte values for the characters in Hello.
How does it work?
String Literal
"Hello"
|
v
UTF-8 Literal Suffix (u8)
|
v
UTF-8 Encoded Bytes
|
+-- H --> 72
+-- e --> 101
+-- l --> 108
+-- l --> 108
+-- o --> 111
|
v
ReadOnlySpan<byte>
Example: Compare UTF-8 Data
ReadOnlySpan<byte> expected = "SUCCESS"u8;
ReadOnlySpan<byte> actual = "SUCCESS"u8;
bool isEqual = actual.SequenceEqual(expected);
Console.WriteLine(isEqual);
Output
True
UTF-8 Literal vs Encoding.UTF8.GetBytes()
|
Feature |
UTF-8 Literal ( |
|
|---|---|---|
|
Introduced |
C# 11 |
Earlier .NET versions |
|
Input |
Compile-time string literal |
Runtime string |
|
Result |
Read-only UTF-8 byte data |
Usually |
|
Allocation |
Can avoid per-call byte-array allocation |
Returning |
|
Best use |
Constant UTF-8 text |
Dynamic strings |
Important Limitation
UTF-8 literals cannot directly interpolate runtime variables.
This is invalid:
string name = "John";
var data = $"Hello {name}"u8; // Compilation error
For dynamic strings, use:
using System.Text;
string name = "John";
byte[] data = Encoding.UTF8.GetBytes($"Hello {name}");
Key Points
-
Introduced in C# 11.
-
Uses the
u8suffix. -
Produces UTF-8 encoded byte data.
-
Commonly assigned to
ReadOnlySpan<byte>. -
Can reduce runtime encoding overhead and allocations for constant text.
-
Does not support string interpolation.
-
Useful for high-performance APIs, networking, and binary data processing.
Interview Tip: UTF-8 String Literals are useful when working with constant UTF-8 byte sequences because they can avoid repeated runtime string-to-byte conversions.
C# 11.0 – Quick Revision
|
Feature |
Main purpose |
Important syntax |
|---|---|---|
|
Required Members |
Enforce initialization |
|
|
Raw String Literals |
Simplify multiline and special-character strings |
|
|
Generic Math |
Arithmetic with generic numeric types |
|
|
List Patterns |
Match collection structure and elements |
|
|
UTF-8 String Literals |
Create UTF-8 byte data from literals |
|
Most Important Interview Questions
-
What is the difference between
requiredandinit? -
What are Raw String Literals, and how do they differ from verbatim strings?
-
How does Generic Math work using
INumber<T>? -
What are static abstract interface members, and why are they important for Generic Math?
-
What are List Patterns, and what is the purpose of
..? -
What is the difference between UTF-8 String Literals and
Encoding.UTF8.GetBytes()?
For a Senior .NET Developer or Technical Lead interview, prioritize Required Members, Generic Math, and List Patterns, while understanding the practical use cases of Raw String Literals and UTF-8 String Literals.