Skip to main content

26. Modern C# Features

Level: Beginner to Intermediate
Goal: Understand modern C# features in simple words before using them in real projects.

ℹ️ What You'll Learn
  • What modern C# features are
  • Why records reduce repeated code
  • What pattern matching does
  • What init and required properties mean
  • When to learn each feature

C# keeps improving.

Modern C# features help us write:

  • Less code
  • Cleaner code
  • Safer code
  • More readable code

Do not worry about memorizing all features today. First understand where each feature is useful.

Quick Definitions

  • Tuple - Return multiple values from method as grouped package
  • Record - Simple data class with automatic equality, ToString, hashing
  • Pattern matching - Check object properties/values with is, switch
  • init property - Set value only during object creation, cannot change after
  • required property - Compiler forces this value to be provided
  • Primary constructor - Constructor parameters become field initializers automatically
  • Deconstruction - Unpack tuple into separate variables
  • Immutable - Cannot be changed after creation
  • Syntactic sugar - Shorter syntax for common pattern
  • Nullable - Can be null or have value

What We Will Learn

FeatureSimple Meaning
TupleReturn more than one value from a method
RecordCreate simple data objects with less code
Pattern matchingCheck object shape or value clearly
init propertySet a value only during object creation
required propertyForce important values to be provided
Primary constructorShorter constructor syntax

Tuples

A tuple can hold multiple values together.

Example: A method can return pass status, grade, and percentage.

static (bool passed, string grade, double percentage) CalculateResult(int mark)
{
bool passed = mark >= 35;
string grade = mark >= 90 ? "A+" :
mark >= 75 ? "A" :
mark >= 35 ? "Pass" : "Fail";

return (passed, grade, mark);
}

var result = CalculateResult(86);

Console.WriteLine(result.passed);
Console.WriteLine(result.grade);
Console.WriteLine(result.percentage);

You can also split the tuple into variables.

var (passed, grade, percentage) = CalculateResult(86);

Console.WriteLine($"{grade} - {percentage}");

Use tuples when the returned values are small and closely related.

Records

A record is a simple way to create a data object.

public record StudentDto(int Id, string Name, string ClassName);

This one line creates a data type with:

  • Properties
  • Constructor
  • Useful ToString()
  • Value-based comparison

Example:

var student1 = new StudentDto(1, "Sahasra", "10th");
var student2 = new StudentDto(1, "Sahasra", "10th");

Console.WriteLine(student1 == student2); // True
Console.WriteLine(student1);

Records are useful for:

  • API responses
  • DTOs
  • Read-only data
  • Small data models

Record with With Expression

Records are usually used as immutable data.

If you need a changed copy, use with.

var student = new StudentDto(1, "Sahasra", "10th");

var promotedStudent = student with { ClassName = "11th" };

Console.WriteLine(student.ClassName); // 10th
Console.WriteLine(promotedStudent.ClassName); // 11th

The original object is not changed.

Pattern Matching

Pattern matching helps us write cleaner condition checks.

Example:

static string GetResultMessage(int marks)
{
return marks switch
{
>= 90 => "Excellent",
>= 75 => "Very good",
>= 35 => "Pass",
_ => "Fail"
};
}

Console.WriteLine(GetResultMessage(82));

This is easier to read than many if-else blocks.

Object Pattern Matching

You can check object properties directly.

public class Student
{
public string Name { get; set; } = "";
public double Percentage { get; set; }
public bool FeesPaid { get; set; }
}

static string GetStudentStatus(Student student)
{
return student switch
{
{ Percentage: >= 90, FeesPaid: true } => "Top student and fees clear",
{ Percentage: >= 35, FeesPaid: true } => "Passed and fees clear",
{ Percentage: >= 35, FeesPaid: false } => "Passed but fees pending",
_ => "Needs attention"
};
}

Init Properties

init means a property can be set only when creating the object.

public class Student
{
public int Id { get; init; }
public string Name { get; init; } = "";
public string ClassName { get; set; } = "";
}

var student = new Student
{
Id = 1,
Name = "Sahasra",
ClassName = "10th"
};

student.ClassName = "11th"; // allowed
// student.Id = 2; // not allowed after creation

Use init for values that should not change later, like ID.

Required Properties

required tells C# that a value must be given when creating the object.

public class Teacher
{
public required string Name { get; set; }
public required string Subject { get; set; }
}

var teacher = new Teacher
{
Name = "Anitha",
Subject = "Maths"
};

If Name or Subject is missing, C# shows a compile-time error.

Primary Constructors

Primary constructors make constructor code shorter.

Traditional style:

public class FeeService
{
private readonly string _schoolName;

public FeeService(string schoolName)
{
_schoolName = schoolName;
}
}

Modern style:

public class FeeService(string schoolName)
{
public void PrintSchool()
{
Console.WriteLine(schoolName);
}
}

You will see this more in newer .NET projects.

School Management Example

Use tuple, record, and pattern matching together.

💻 Try It — Console App
💡 Paste into Program.cs and run⌥ GitHub
public record StudentResult(string Name, int Marks);

static (bool passed, string grade) CalculateGrade(int marks)
{
string grade = marks switch
{
>= 90 => "A+",
>= 75 => "A",
>= 35 => "Pass",
_ => "Fail"
};

return (marks >= 35, grade);
}

var student = new StudentResult("Sahasra", 86);

var (passed, grade) = CalculateGrade(student.Marks);

Console.WriteLine($"Student: {student.Name}");
Console.WriteLine($"Marks: {student.Marks}");
Console.WriteLine($"Passed: {passed}");
Console.WriteLine($"Grade: {grade}");

When You'll Use This in SMS

Modern C# simplifies SMS code.

Tuples return multiple values:

// Return pass status, grade, percentage
var (passed, grade, pct) = CalculateStudentResult(marks);

Records simplify data objects:

public record StudentDto(int Id, string Name, string ClassName);
public record FeeDto(int StudentId, decimal Amount, bool Paid);

Pattern matching validates cleanly:

if (student is { ClassName: "10-A", Status: "Active" })
ProcessStudent(student);

init prevents accidental changes:

public record Student
{
public string Name { get; init; } // Set once, never change
}

Real impact: Modern features reduce 50 lines of boilerplate to 5 lines.


Try This Now

  1. Create tuple returning (status, grade, percentage)
  2. Deconstruct: var (pass, grade, pct) = Calculate(85);
  3. Create record: record Student(int Id, string Name);
  4. Use pattern matching to check student properties
  5. Compare old class vs new record versions

ℹ️ Video Tutorial

Modern C# features explained: Tuples, Records, Pattern Matching, init properties, required properties, primary constructors. Video coming soon. Subscribe to NexCoding YouTube for updates.


Common Mistakes

Mistake 1: Using tuples for too much data.

return (id, name, className, section, phone, email, address);

Better: create a class or record.

Mistake 2: Using records for behavior-heavy objects.

public record FeeService();

Better: use class for services.

Mistake 3: Forgetting the default case in switch expressions.

string result = marks switch
{
>= 35 => "Pass"
};

Better:

string result = marks switch
{
>= 35 => "Pass",
_ => "Fail"
};

Best Practices

  • Use tuples for small multiple return values.
  • Use records for DTOs and simple data.
  • Use classes for business logic and services.
  • Use pattern matching when it improves readability.
  • Always include _ default in switch expressions.
  • Use init for values that should not change.
  • Use required for mandatory properties.
  • Learn modern features slowly and apply them where they make code clearer.

Practice Task

Create a small program that:

  1. Creates a record called ExamResult.
  2. Stores student name and marks.
  3. Uses pattern matching to find grade.
  4. Returns grade and pass status using a tuple.

Quick Revision

  • Tuple returns multiple values.
  • Record creates simple data objects.
  • Pattern matching makes conditions cleaner.
  • init protects values after object creation.
  • required forces important values.
  • Primary constructors reduce constructor code.
🎯 Q1: What is a tuple in C#?

A tuple is a small group of values. It is useful when a method needs to return more than one value.

🎯 Q2: What is a record?

A record is a simple data type. It is commonly used for DTOs, API responses, and read-only data.

🎯 Q3: Difference between class and record?

Use a class for behavior and business logic. Use a record for simple data that mainly stores values.

🎯 Q4: What is pattern matching?

Pattern matching is a clean way to check values, types, or object properties and return results.

🎯 Q5: What is the use of required?

required forces the developer to provide a property value when creating an object.

🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Modern C# features. Try these prompts:

  • "Explain C# records with school examples"
  • "Explain tuples in simple words with examples"
  • "Give me beginner exercises for C# pattern matching"
  • "When should I use init and required in C#?"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

Next Article

Extension Methods ->

nexcoding.in