Source code
.NET Best Practices C# Visual Studio

How to Use Collection Expressions in C# 12.0

Welcome to today’s post.

In today’s post I will be explaining a new feature in C# 12, which is also in the release for .NET 8.

The feature I will be explaining today are Collection Expressions.

Now, Collection Expressions are not a new piece of functionality or tool that changes the result of the processes within our application. They are a different way to achieve the same outcome when preparing data within collection objects our application. The most important aspect behind Collection Expressions is that they make our code look cleaner. You will see what I mean in the examples that follow.

The Traditional Approach to Creating and Initializing Data in Collections

You will be familiar with the standard pattern (or idiom) of creating object instances from classes in C#, then populating the collection within the object with one or more instances of data. This requires two steps. You would first create the instance, then do one of the following:

  1. Initialise each data entry that will be added to the collection.
  2. Use a method to add each data entry to the object collection within a loop.

OR

Add all data entries into the collection inline.

There are maybe two or three steps to achieve the initialisation and population of each collection.

If we repeat the above every time, when we access our collection, then there would be memory overheads in the heap.

An example of the standard approach using the inline population method is below where I am creating lookups based on a Dictionary collection. This might be in a class, where the lookups are populated from a call within the class constructor. The inline population of the collection initialization is also known as a collection initializer.

public class BookCreateModel
{
    Dictionary<int, string> _media_types;
    Dictionary<int, string> _genres;
    …

    public BookCreateModel(BookViewModel vm)
    {            
        …
        CreateLookups();
    }

    private void CreateLookups()
    {
        this._genres = new Dictionary<int, string>()
        {
            { 1, "Fiction" },
            { 2, "Non-Fiction" },
            { 3, "Educational" },
            { 4, "Childrens" },
            { 5, "Family" },
            { 6, "Fantasy" },
            { 7, "Finance" },
            { 8, "Cooking" },
            { 9, "Technology" },
            { 10, "Gardening" }
        };

        this._media_types = new Dictionary<int, string>()
        {
            { 1, "Book" },
            { 2, "DVD" },
            { 3, "CD" },
            { 4, "Magazine" }
        };
    }
	…
}

If the above lookup data entries were taken from a data source (SQL backend, a file, or a web service), then we could use a loop to iterate through the retrieved data source entries and append them to the collection.

Simplified Approach to Creating Collection Instances

There is an additional level of simplification that can be used to create the objects. The ability to create an instance of classes without specifying its type. We all know how to use the new operator and we do it subconsciously. It is even built-into the C# complier completion when editing.

We also have a new() operator, which allows creation of the class without specifying its type. The class type in this case is inferred from the context.  

Below is the lookup creation using the new() operator:

private void CreateLookups()
{
    this._genres = new()
    {
        { 1, "Fiction" },
        { 2, "Non-Fiction" },
        { 3, "Educational" },
        { 4, "Childrens" },
        { 5, "Family" },
        { 6, "Fantasy" },
        { 7, "Finance" },
        { 8, "Cooking" },
        { 9, "Technology" },
        { 10, "Gardening" }
    };

    this._media_types = new()
    {
        { 1, "Book" },
        { 2, "DVD" },
        { 3, "CD" },
        { 4, "Magazine" }
    };
}

Can we improve the above initialization to make it cleaner?

I will show how in the next section.

Collection Expressions

We can try using Collection Expressions.

Collection expressions are a change to way in which collections are initialized.

Instead of using the braces { … } to wrap the entries, we use brackets [ … ] to wrap the entries.

A simple example is an array of integers.

Below is an array of allocated integers:

int[] ratings = new int[] { 1, 2, 3, 4 };

Using Collection Initializers, the above transforms to:

int[] ratings = { 1, 2, 3, 4, 5 };

With Collection Expressions, the above transforms to:

int[] ratings = [1, 2, 3, 4];

Below is a List of integers using the traditional allocation method:

List<int> ratings = new List<int> { 1, 2, 3, 4, 5 };

Using the new() operator, the above transforms to:

List<int> ratings = new() { 1, 2, 3, 4, 5 };

With Collection Expressions, the above transforms to:

List<int> ratings = [ 1, 2, 3, 4, 5 ];

With the Dictionary<T> collections, we can try using the following syntax:

this._genres = 
[
    { 1, "Fiction" },
    { 2, "Non-Fiction" },
    { 3, "Educational" },
    { 4, "Childrens" },
    { 5, "Family" },
    { 6, "Fantasy" },
    { 7, "Finance" },
    { 8, "Cooking" },
    { 9, "Technology" },
    { 10, "Gardening" }
]; 

The above does not parse. So, what else can we try?

We can instead of using a Dictionary type, try a List type to store our key-value pairs.    

Below is an example of a key-value pair:

{ 1, “Fiction” } 

This corresponds to an instance of the static class KeyValuePair.

In the amended lookup method, we can create instances of the key-value pair entries:

var keyValuePair1 = KeyValuePair.Create(1, "Fiction");
var keyValuePair2 = KeyValuePair.Create(2, "Non-Fiction");
var keyValuePair3 = KeyValuePair.Create(3, "Educational");
…

Declare the collection list as shown:

List<KeyValuePair<int, string>> _genres;

Add entries to the collection list as shown with the collection initializer:

this._genres = new()
{
    keyValuePair1,
    keyValuePair2,
    keyValuePair3,
    …
};

The above is equivalent to:

this._genres = new()
{
    KeyValuePair.Create(1, "Fiction"),
    KeyValuePair.Create(2, "Non-Fiction"),
    KeyValuePair.Create(3, "Educational")
    …
};

We can further simplify the above by removing the new() operator and replace the braces with brackets to leave us with:

this._genres =
[
    KeyValuePair.Create(1, "Fiction"),
    KeyValuePair.Create(2, "Non-Fiction"),
    KeyValuePair.Create(3, "Educational")
    …
];

Replacing each entry with the variable gives:

this._genres = new()
{
    keyValuePair1,
    keyValuePair2,
    keyValuePair3,
    …
};

With the collection expression syntax, the above transforms to:

this._genres = 
[
    keyValuePair1,
    keyValuePair2,
    keyValuePair3,
    …
];

We have seen how the use of collection expressions can simplify the creation and initialisation of objects in C# 12.0.

With collections of integers and strings, the conversion is straightforward. With more complex collections such as Dictionary types with multi-values entries, the conversion is a little more cumbersome.

Borrowing the Simplified Syntax from TypeScript

The introduction of collection expressions for C# is like the syntax used in the TypeScript language that is used in popular front-end development frameworks such as Angular. The use of braces to declare and initialize values with arrays is shown with the following examples below:

let genres: string[] = ["Fiction", "Science", "Travel" ];
let ratings: number[] = [ 1, 2, 3, 4, 5 ];

As many full-stack developers switch between a backend language like C# to a front-end language, it reduces the learning curve for the developer and makes the code look cleaner.

That is all for today’s post.

I hope you have found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial