A C# 6 gotcha: Initialization vs. Expression Bodied Members

I was asked to review a bit of code for a friend the other day, and the result may be illustrative for others. I’ve stripped out much of the code to simplify the question. Examine the following small program:

 

public class SomeContainer

{

    public IList<int> SomeNumbers => new List<int>();

}

class Program

{

    static void Main(string[] args)

    {

        var container = new SomeContainer();

        var range = Enumerable.Range(0, 10);

        foreach (var item in range)

            container.SomeNumbers.Add(item);

 

        Console.WriteLine(container.SomeNumbers.Count);

    }

}

 

If you run this sample, you’ll find that container.SomeNumbers has 0 elements.

 

How is that possible? Why does the container not have 10 elements?

 

The problem is this line of code:

 

public IList<int> SomeNumbers => new List<int>();

 

The author had used an expression bodied member when he meant to use an initializer for auto-property.

An expression bodied member evaluates the expression whenever the public member is accessed. That means that every time the SomeNumbers property of the container gets accessed, a new List<int> is allocated, and assigned to the hidden backing field for the SomeNumbers property. It’s as though the author wrote this:

 

public IList<int> SomeNumbers { get { return new List<int>(); } }

 

When you see it using the familiar syntax, the problem is obvious.

This fix is also obvious. Use the syntax for an initializer:

 

public IList<int> SomeNumbers { get; } = new List<int>();

Notice the changes from the original code. SomeNumbers is now a read only auto property. It also has an initializer, This would be the equivalent of writing:

 

public IList<int> SomeNumbers { get { return storage; } }

private readonly List<int> storage = new List<int>();

 

That expresses the design perfectly.

 

This just illustrates that as we get new vocabulary in our languages, we need to fully understand the semantics of the new features. We need to make sure that we correctly express our designs using the new vocabulary.

It’s really easy to spot when you make a mistake like this: every time you look at this property in the debugger, you’ll see that it re-initializes the property. That’s the clue that you’ve made this mistake.

 

 

Created: 7/16/2015 5:05:29 PM

Current Projects

I create content for .NET Core. My work appears in the .NET Core documentation site. I'm primarily responsible for the section that will help you learn C#.

All of these projects are Open Source (using the Creative Commons license for content, and the MIT license for code). If you would like to contribute, visit our GitHub Repository. Or, if you have questions, comments, or ideas for improvement, please create an issue for us.

I'm also the president of Humanitarian Toolbox. We build Open Source software that supports Humanitarian Disaster Relief efforts. We'd appreciate any help you can give to our projects. Look at our GitHub home page to see a list of our current projects. See what interests you, and dive in.

Or, if you have a group of volunteers, talk to us about hosting a codeathon event.