As C# continues to evolve, each new version introduces powerful new features that increase the power and readability of the language. You can achieve more efficient, maintainable, and expressive code by moving from older versions of C# to C# 12. You can explore the features of .NET 8 here.

Many developers still use older versions of C# due to legacy codebases, corporate constraints, and familiarity with older language features. Upgrading can seem daunting, but modern versions of C# offer significant benefits such as better performance, enhanced features and improved security.
By gradually adopting these features through incremental refactoring, pilot projects and team training, codebases can be efficiently modernized. This strategic approach not only simplifies development, but also future-proofs applications, ensuring they remain robust and relevant in a rapidly evolving technology landscape.

This article will guide you through converting C# code from earlier versions to C# 12, highlighting the latest features and how they can replace older constructs. Check this article for .NET 9 Preview

In spite of the beneficial features available in the latest version of C#, many developers still find themselves working with older versions of the language.
There are many reasons for this, including legacy codebases, business constraints and familiarity with existing patterns. Let’s explore why this is, and how developers can gradually adopt the latest C# features to keep their code modern and efficient.

Why Developers Still Use Older Versions of C#

Legacy codebases:
Many organizations have extensive code bases that were written in older versions of C#.
The upgrade of such code bases can be a daunting task and is often a significant investment of time and effort. Other reasons for sticking with older versions include the fear of breaking existing functionality and the need for extensive testing to ensure stability.

Older versions of code on the web:
Most of the code available on the web and in public repositories is still in the older version of C#, and newcomers research and find the same older version that works for their needs.

Enterprise constraints:
Large organizations often have strict policies and governance around the adoption of new technologies.
To minimize the risks associated with upgrading to the latest versions, they may prefer stable and thoroughly tested versions of languages and frameworks.

Familiarity and skills:
Developers who have worked with a particular version of C# for a long time may be more comfortable with its syntax and features. Learning new language features and paradigms can require a steep learning curve that not all developers or teams are ready to take on immediately.

Tool and framework compatibility:
Some development tools and frameworks may not be immediately compatible with the latest versions of C#. Ensuring that all necessary tools and libraries work with a new version can delay its adoption.

Cost of migration:
The process of upgrading a large code base to a newer version of C# can be expensive. It involves not only rewriting parts of the code, but also testing, debugging and possibly training developers on the new features.

Let’s commence!! The following are the best and easiest ways to make the transition from the older C# version to C# 12.

Primary Constructors for Classes and Structs

Old C# (before C# 12):
In earlier versions of C#, constructors required a lot of boilerplate code. Here’s an example of a typical class with properties initialized by a constructor.

public class Person
{
    public string Name { get; }
    public int Age { get; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

New C# 12:

C# 12 introduces primary constructors, allowing properties to be initialized directly in the class declaration.

public class Person(string Name, int Age);

This not only reduces boilerplate code but also makes the code more concise and readable.

Default Parameter Values for Lambda Expressions

Old C# (before C# 12):
Lambda expressions couldn’t have default parameter values. You had to handle defaults separately, which often meant writing more code.

Func<int, int> square = x => x * x;
Func<int, int, int> add = (x, y) => x + y;

int defaultY = 10;
int result = add(5, defaultY);

New C# 12:

In C# 12, you can define default parameter values for lambda expressions directly.

Func<int, int, int> add = (x, y = 10) => x + y;
int result = add(5); // y defaults to 10, result is 15

This simplifies the function definitions and makes the code cleaner.

Using Alias for Any Type

Old C# (Before C# 12):

You could only use the using directive for namespace or named types.

using ListOfStrings = System.Collections.Generic.List<string>;

ListOfStrings myList = new ListOfStrings();

New C# 12:

C# 12 expands the using directive to alias any type, including generic types.

using System;
using MyInt = System.Int32;

MyInt number = 10;
Console.WriteLine(number);

This makes type names more flexible and the code more readable, especially when working with complex types.

Enhanced Pattern Matching

Old C# (Before C# 12):

Pattern matching was somewhat limited in earlier versions. You had to write more verbose code for certain scenarios.

object obj = 5;
if (obj is int num && num > 0)
{
    Console.WriteLine($"{num} is a positive number");
}

New C# 12:

C# 12 introduces more powerful pattern matching features, making the code more expressive.

object obj = 5;
if (obj is int num and > 0)
{
    Console.WriteLine($"{num} is a positive number");
}

The pattern matching is more succinct and easier to read, allowing for more complex patterns to be expressed simply.

Improved Collection Expressions

Old C# (before C# 12):
Collection initializers used to be very verbose, especially for nested collections or complex initializers.

var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = new List<int>();
foreach (var num in numbers)
{
    if (num % 2 == 0)
    {
        evenNumbers.Add(num);
    }
}

New C# 12:

C# 12 introduces improvements in collection expressions, allowing you to write more concise and declarative code.

var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0).ToList();

This reduces boilerplate code and makes it easier to work with collections.

File-scoped Namespaces

Old C# (Before C# 12):

Every namespace had to be explicitly opened and closed with braces.

namespace MyApp
{
    public class MyClass
    {
        // Class content
    }
}

New C# 12:

File-scoped namespaces allow you to declare the namespace for the whole file with a single line, reducing indentation.

namespace MyApp;

public class MyClass
{
    // Class content
}

This change reduces the boilerplate code and improves code readability, especially in larger files.

Readonly Structs and Records

Old C# (Before C# 12):

Making a struct or a class immutable required multiple steps and explicit readonly keywords.

public struct ReadonlyPoint
{
    public readonly int X;
    public readonly int Y;

    public ReadonlyPoint(int x, int y)
    {
        X = x;
        Y = y;
    }
}

New C# 12:

C# 12 simplifies this with readonly structs and records.

public readonly struct ReadonlyPoint(int X, int Y);

public readonly record Person(string Name, int Age);

This makes it straightforward to declare immutable data types.

Top-level Statements

Old C# (Before C# 12):

Every program needed a Main method as the entry point.

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
    }
}

New C# 12:

C# 12 allows top-level statements, simplifying the structure of simple programs.

Console.WriteLine("Hello, World!");

This reduces boilerplate code, making it easier to write and understand simple scripts.

There are many benefits to upgrading to C# 12, from reducing boilerplate code to making your codebase more readable and maintainable.

Example Code Transformation

Here’s a complete example of converting an old C# base to C# 12 using the new features:

Old C# Code:

public class Customer
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Customer(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public void PrintDetails()
    {
        Console.WriteLine($"Customer: {Name}, Age: {Age}");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        Customer customer = new Customer("Alice", 30);
        customer.PrintDetails();
    }
}

Converted to C# 12:

public class Customer(string Name, int Age)
{
    public void PrintDetails() => Console.WriteLine($"Customer: {Name}, Age: {Age}");
}

Customer customer = new("Alice", 30);
customer.PrintDetails();

This transformation not only reduces the lines of code but also leverages the expressive power of C# 12, leading to a cleaner and more maintainable codebase.

Using the latest C# features ensures that your code remains modern, efficient and easy to maintain, bringing long-term benefits to your projects and teams.

Final Thoughts

Moving to C# 12 has many benefits, from reducing boilerplate code to making your codebase easier to read and maintain. The new features in C# 12 provide more expressive syntax and powerful constructs that streamline many common programming tasks.

By taking advantage of primary constructors, improved pattern matching, enhanced collection expressions, and more, you can write cleaner, more efficient code. As C# continues to evolve, staying current with the latest features will help you maintain a modern and robust codebase.

By Rijwan Ansari

Research and Technology Lead | Software Architect | Full Stack .NET Expert | Tech Blogger | Community Speaker | Trainer | YouTuber. Follow me @ https://rijsat.com Md Rijwan Ansari is a high performing and technology consultant with 10 plus years of Software Development and Business Applications implementation using .NET Technologies, SharePoint, Power Platform, Data, AI, Azure and cognitive services. He is also a Microsoft Certified Trainer, C# Corner MVP, Microsoft Certified Data Analyst Associate, Microsoft Certified Azure Data Scientist Associate, CSM, CSPO, MCTS, MCP, with 15+ Microsoft Certifications. He is a research and technology lead in Tech One Global as well as leading Facebook community Cloud Experts Group and SharePoint User Group Nepal. He is a active contributor and speaker in c-sharpcorner.com community, C# Corner MVP and his rank at 20 among 3+ millions members. Additionally, he is knee to learn new technologies, write articles, love to contribute to the open-source community. Visit his blog RIJSAT.COM for extensive articles, courses, news, videos and issues resolution specially for developer and data engineer.

Leave a Reply

Your email address will not be published. Required fields are marked *