C# 7 Proposal: With-Expressions

//Build changes Everything

I started this series after giving a presentation at NDC London on the potential features up for discussion in C# 7. That information was based on the public design discussions on GitHub. Now that //build has happened, we know a bit more about the plans. There is even a public preview of Visual Studio 15 available for download. There are two versions: a ‘classic’ installer, and a new lightweight installer that runs much more quickly but has fewer scenarios supported.

I have installed both installers of Visual Studio 15 on my machine. They install side-by-side, and my machine works well. (It is also a machine with Visual Studio 2015 on it, and that’s been unaffected. This is still pre-release software, and you should proceed with some caution.

All of which means that there are two important updates to this series: First, the plans have been updated. The team announced at //build that the languages will have a faster cadence than before. That’s great news. But, it comes at a price. Some of the features that were slated for C# 7 are likely to be pushed to the release that follows C# 7. Private Protected is one of those features. Non-Nullable Reference Types (covered in my NDC talk, but not yet in this blog series) may be another.

Immutable Types and With Expressions

Now that those announcements are made, let’s discuss the addition of ‘with expressions’ to make it easier to work with immutable types.

Immutable types are becoming a more common part of our design toolkit. Immutable types make it easier to manage multi threaded code. Shared data does not create issues when that data can’t change.

However, working with immutable types can become very cumbersome. Making any change means making a new object, and initializing it with all the properties of the original object, except the one you want to change.

With expressions are meant to address this issue. In their most basic use, consider that you have created an immutable Person object:

var scott = new Person(“Scott”, “Hanselman”);

Later, you find you need an object that’s almost the same, but must have a different last name:

var coolerScott = scott with { LastName = “Hunter” };

This simple example shows the syntax, but doesn’t provide great motivation for using the feature. It’s almost as simple to create a new object and explicitly set the two fields by calling the constructor. With expressions become much more useful in real world scenarios where more fields are needed to initialize the object. Imagine a more extensive Person class that included employer, work address and so on. When a Person accepts a new role, the code to create the new object becomes much more heavyweight. And it’s all boilerplate code. That represents a lot of busy work that adds minimal value. In those cases, With expressions are your friend.

This feature will leverage a convention found throughout the Roslyn APIs. You may have seen that many types have .With() methods that create a new object by copying an existing object and replacing one property. The proposed feature would use a With() method if one was available. If not, one proposal would generate a call to a constructor and explicitly set all the properties. Another concept would only support types that had an appropriate With() method.

The syntax for With expressions was originally proposed for record types (which I will cover in a future blog post). Record types are a new feature, and the compiler can generate all the necessary code to support new syntax like With expressions. The current proposal would specify that Record types would generate With() methods that would support this language feature.

When With Expressions are applied to record types, the generated With() method provides a great example of how such a method can be generated that would support many permutations of With Expressions. That proposal minimizes the amount of work necessary to support a full set of With Expressions for all combinations of updated properties.

Open Questions

In the previous section, I said that one proposal would fall back to a constructor if a With() method was not available. The advantage to that design is that With Expressions would work with all existing types. The advantage of requiring a With() method is that it enables richer support for positional and name mapping.

But there are more questions. In the scenario above, suppose the Person type was a base class for other types: Teacher, Student, Teaching Assistant, Tutor, Advisor. Should a With Expression that uses a variable of type ‘Person’ work correctly on any derived type? There’s a goal to enable those scenarios. You can read about the current thinking in the February C# Design Notes.

With Expressions are one language feature that will make working with immutable types more pleasant and natural in C#. These features will make it easier to create the designs we want to support. It’s part of that “Pit of Success” design goal for C#: Make it easier to do the proper design.

Most importantly, these issues are still being discussed and debated. If you have ideas, visit the links I’ve put in place above. Participate and add your thoughts.

Created: 4/12/2016 8:48:30 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.