C# 12 New Features in 2025: What’s New with .NET 8

Hey there, fellow coders. As a software engineer and blogger, I’m always excited to dive into the latest tools and features that make our lives easier and C# 12 with .NET 8 in 2025 does not disappoint.
In this article, I’ll walk you through the top C# 12 new features released alongside .NET 8 in November 2023.
So, what’s the big deal? C# 12 simplifies syntax, reduces boilerplate code, boost performance, and introduces features that make your codebase cleaner, more expressive and ultimately make your applications more modern and flexible.

Let’s dive into what’s new in 2025 and why you should care.

Top C# 12 New Features to Master in 2025

Here’s a rundown of the C# 12 features that every .NET developer should know. I’ve included code snippets to show you how they work in action because nothing beats seeing the magic happen.

What Are Primary Constructors in C# 12?

Primary constructors are a new feature in C# 12 that allow you to define constructor parameters directly in the class or struct declaration and eliminating the need for a separate constructor block.

This feature was previously available for record types in C# 9 but is now extended to regular classes and structs in C# 12.

The parameters you define become available as private fields throughout the type, and you can use them to initialize properties, fields, or even perform logic all without writing a traditional constructor.

Why is this Important?

✅ Reduces boilerplate code: When using Primary Constructors, we no need to declare instance variables separately.
✅ Cleaner Code: Your class definition becomes more concise and readable.
✅ Flexibility: Works seamlessly with dependency injection or simple data classes.
✅ Works with classes and structs, unlike previous versions where Primary Constructors was limited to records.

Syntax Before C# 12:

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

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

📌New Syntax in C# 12 (Primary Constructors):

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

Here, Name and Age are automatically assigned as properties, so explicit field assignments are not required.

C# 12 Collection Expressions

What Are Collection Expressions?

Collection expressions let you create arrays, lists, or spans with a cleaner, more concise syntax. Say goodbye to clunky initialization code as this feature reduces boilerplate code and enhances readability.

Syntax Before C# 12:

List<int> numbers = new List<int> { 10, 20, 30, 40, 50 };
List<string> names = new List<string>{"Ali", "Boby", "Mohit"};

📌New Syntax in C# 12 (Collection Expressions):

int[] numbers = [10, 20, 30, 40, 50]; // New syntax
List<string> names = ["Ali", "Boby", "Mohit"];

Console.WriteLine(string.Join(", ", numbers)); // Output: 10, 20, 30, 40, 50
Console.WriteLine(string.Join(", ", names));  // Output: Ali, Boby, Mohit

The Benefits of using Collection Expressions include enhanced code readability and maintainability, along with seamless compatibility across arrays, lists, spans, queues, and hash sets.

C# 12 Inline Arrays: Boost Performance Fast

Inline Arrays in C# 12 provide a mechanism for declaring fixed-size arrays directly within structs. This feature is designed to enhance performance by eliminating heap allocations and relying on stack memory.
Inline arrays are particularly useful in scenarios where you need a small, fixed-size collection of elements without the overhead of heap allocation.

Benefits of Inline Arrays:

✅ No heap allocation: Unlike regular arrays or lists that stored on the heap, inline arrays store data directly inside a struct. That means less memory overhead and no garbage collection hassles.
✅ Enhanced Performance: The inline arrays can significantly boost execution speed and reduce memory pressure by avoiding heap allocations.

Defining an Inline Array in C# 12

To declare an inline array, use the System.Runtime.CompilerServices.InlineArray attribute.

using System.Runtime.CompilerServices;

[InlineArray(4)]  // Declares an inline array with a fixed size of 4
public struct MyInlineArray
{
    private int _element; // Required backing field
}

📌 Using an Inline Array in C# 12:

MyInlineArray numbers = default;
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;

Console.WriteLine(numbers[1]); // Output: 20

C# 12 Default Lambda Parameters

What Are Optional Parameters in Lambda Expressions?

Lambdas in C# 12 now support default parameters, just like regular methods. This means you can write more reusable and flexible lambda expressions.

Code Example 1:

var greet = (string name = "Guest") => $"Hello, {name}.";
Console.WriteLine(greet());        // Output: Hello, Guest.
Console.WriteLine(greet("Ali")); // Output: Hello, Ali.

Code Example 2:

using System;

namespace LambdaDefaultDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Lambda with two parameters, both with defaults
            var addNumbers = (int a = 10, int b = 5) => a + b;

            // Test with default values
            Console.WriteLine("Using defaults: " + addNumbers());        // Output: 15 (10 + 5)

            // Test with one override
            Console.WriteLine("One override: " + addNumbers(7));         // Output: 12 (7 + 5)

            // Test with both overridden
            Console.WriteLine("Both overridden: " + addNumbers(3, 4));   // Output: 7 (3 + 4)
        }
    }
}

Calling Options:

  • addNumbers() uses defaults: 10 + 5 = 15.
  • addNumbers(7) overrides a with 7, keeps b as 5: 7 + 5 = 12.
  • addNumbers(3, 4) overrides both: 3 + 4 = 7.

C# 12 Alias Any Type

In C# 12, Microsoft has enhanced the using directive by allowing it to alias any type, not just named types, namespaces or generics. This provides better readability, reduced redundancy, and improved code maintainability.

This is a small but mighty feature for improving code readability.

Syntax Before C# 12

Previously, aliases were limited to named types.

using MyAlias = System.Collections.Generic.Dictionary<int, string>;

MyAlias dict = new MyAlias();

📌 New Syntax in C# 12 (Alias Any Type)

Now, aliases can be applied to constructed types, pointers, arrays, and even tuples

using StringList = System.Collections.Generic.List<string>; // Alias for a constructed generic type
using NumberArray = int[];       // Alias for an array type
using NameAgeTuple = (string Name, int Age); // Alias for a tuple type
using Point = (int X, int Y);
namespace AliasAnyType
{
    class Program
    {       

    static void Main(string[] args)
        {
           
            // Usage:
            StringList names = ["Ali", "Boby", "Radha"];
            NumberArray numbers = [10, 20, 30, 40,50];

            NameAgeTuple person = ("John", 30);
            Console.WriteLine($"{person.Name} is {person.Age} years old.");//Output: John is 30 years old.
            Point origin = (0, 0);
            Console.WriteLine($"Origin: ({origin.X}, {origin.Y})"); // Output: Origin: (0, 0)
        }
    }
}

The “Alias Any Type” feature in C# 12 enhances code readability and improves code consistency in large projects by avoiding repetitive long type declarations.

C# 12 Experimental Attribute: Safe API Testing

C# 12 introduces the [Experimental] attribute, it’s a way to flag types, methods, or assemblies as experimental. When you use an API marked with this attribute, the compiler throws a warning to let you know it’s not fully developed yet. This helps indicate that a feature may change or be unstable in future versions.
Think of it as a “use at your own risk” sign. It’s perfect for library authors testing new features without committing to stability.

Why Use the Experimental Attribute?

  1. The Experimental Attribute Warns developers when they use unstable or work in-progress features.
  2. It warns developers and Helps library maintainers to introduce new APIs without breaking changes.
  3. Encourages safe adoption of new functionality in production applications.

How to Use the [Experimental] Attribute in C# 12?

To mark an API as experimental we need to add the [Experimental] attribute from the System.Diagnostics.CodeAnalysis namespace.

Example: Marking a Method as Experimental

using System.Diagnostics.CodeAnalysis;

class Test
{
    [Experimental("This feature is still in testing and may change.")]
    public static void ExperimentalMethod()
    {
        Console.WriteLine("This is an experimental method.");
    }
}

When a developer tries to use this experimental method then they get a compiler warning:

Test.ExperimentalMethod();  // Warning: 'ExperimentalMethod' is experimental: This feature is still in testing and may change.

Example: Marking a Class as Experimental

[Experimental("This class is under development and subject to change.")]
class ExperimentalFeature
{
    public void TestFeature() => Console.WriteLine("Using an experimental feature.");
}

The Experimental Attribute in C# 12 is a game changer for rolling out new APIs with confidence. It lets developers tag classes, methods, or other elements as “still in testing,” triggering compiler warnings to alert against using these unstable features in production. This way, you can experiment and refine new APIs without breaking existing code.

C# 12 and .NET 8 FAQs for 2025 Developers

Q: What are the new features in C# 12?

C# 12 introduces primary constructors, record structs, inline arrays, collection expressions, default lambda parameters, alias any type, and the experimental attribute.

Q: How do I use C# 12 with .NET 8 in 2025?

Install the .NET 8 SDK, use Visual Studio 2022 or later, and set your project to target net8.0 in the .csproj file.

Q: Why use C# 12 inline arrays?

Inline arrays store fixed-size data in structs and skip heap allocation for faster access. it is perfect for high performance .NET 8 applications.

Q: How do C# 12 record structs work?

Record structs are immutable value types with built-in equality and ToString() methods, ideal for lightweight, performance-focused data in .NET 8.

They are stored on the stack which makes them memory efficient for small, short-lived data. Record structs come with value-based equality, meaning two instances with the same data are considered equal, and they also auto-generate a ToString() method for better debugging and logging.

References:

Recommended Articles:

Shekh Ali
5 1 vote
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments