C# 6 in action

Now that the Visual Studio 2015 Preview is available and the C# 6 feature set is a bit more stable, I figured it was time to start updating the Noda Time 2.0 source code to C# 6. The target framework is still .NET 3.5 (although that might change; I gather very few developers are actually going to be hampered by a change to target 4.0 if that would make things easier) but we can still take advantage of all the goodies C# 6 has in store.

I’ve checked all the changes into a dedicated branch which will only contain changes relevant to C# 6 (although a couple of tiny other changes have snuck in). When I’ve got round to updating my continuous integration server, I’ll merge onto the default branch, but I’m in no rush. (I’ll need to work out what to do about Mono at that point, too – there are various options there.)

In this post, I’ll go through the various C# 6 features, and show how useful (or otherwise) they are in Noda Time.

Read-only automatically implemented properties (“autoprops”)

Finally! I’ve been waiting for these for ages. You can specify a property with just a blank getter, and then assign it a value from either the declaration statement, or within a constructor/static constructor.

So for example, in DateTimeZone, this:

private static readonly DateTimeZone UtcZone = new FixedDateTimeZone(Offset.Zero);
public static DateTimeZone Utc { get { return UtcZone; } }

becomes

public static DateTimeZone Utc { get; } = new FixedDateTimeZone(Offset.Zero);

and

private readonly string id;
public string Id { get { return id; } }
protected DateTimeZone(string id, ...)
{
    this.id = id;
    ...
}

becomes

public string Id { get; }
protected DateTimeZone(string id, ...)
{
    this.Id = id;
    ...
}

As I mentioned before, I’ve been asking for this feature for a very long time – so I’m slightly surprised to find myself not entirely positive about the results. The problem it introduces isn’t really new – it’s just one that I’m not used to, as I haven’t used automatically-implemented properties much in a large code base. The issue is consistency.

With separate fields and properties, if you knew you didn’t need any special behaviour due to the properties when you accessed the value within the same type, you could always use the fields. With automatically-implemented properties, the incidental fact that a field is also exposed as a property changes the code – because now the whole class refers to it as a property instead of as a field.

I’m sure I’ll get used to this – it’s just a matter of time.

Initial values for automatically-implemented properties

The ability to specify an initial value for automatically-implemented properties applies to writable properties as well. I haven’t used that in the main body of Noda Time (almost all Noda Time types are immutable), but here’s an example from PatternTestData, which is used to provide data for text parsing/formatting tests. The code before:

internal CultureInfo Culture { get; set; }

public PatternTestData(...)
{
    ...
    Culture = CultureInfo.InvariantCulture;
}

And after:

internal CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture;

public PatternTestData(...)
{
    ...
}

It’s worth noting that just like with a field initializer, you can’t call instance members within the same class when initializing an automatically-implemented property.

Expression-bodied members

I’d expected to like this feature… but I hadn’t expected to fall in love with it quite as much as I’ve done. It’s really simple to describe – if you’ve got a read-only property, or a method or operator, and the body is just a single return statement (or any other simple statement for a void method), you can use => to express that instead of putting the body in braces. It’s worth noting that this is not a lambda expression, nor is the compiler converting anything to delegates – it’s just a different way of expressing the same thing. Three examples of this from LocalDateTime (one property, one operator, one method – they’re not next to each other in the original source code, but it makes it simpler for this post):

public int Year { get { return date.Year; } }

public static LocalDateTime operator +(LocalDateTime localDateTime, Period period)
{
    return localDateTime.Plus(period);
}

public static LocalDateTime Add(LocalDateTime localDateTime, Period period)
{
    return localDateTime.Plus(period);
}

becomes

public int Year => date.Year;

public static LocalDateTime operator +(LocalDateTime localDateTime, Period period) =>
    localDateTime.Plus(period);

public static LocalDateTime Add(LocalDateTime localDateTime, Period period) =>
    localDateTime.Plus(period);

In my actual code, the operator and method each only take up a single (pretty long) line. For some other methods – particularly ones where the body has a comment – I’ve split it into multiple lines. How you format your code is up to you, of course :)

So what’s the benefit of this? Why do I like it? It makes the code feel more functional. It makes it really clear which methods are just shorthand for some other expression, and which really do involve a series of steps. It’s far too early to claim that this improves the quality of the code or the API, but it definitely feels nice. One interesting data point – using this has removed about half of the return statements across the whole of the NodaTime production assembly. Yup, we’ve got a lot of properties which just delegate to something else – particularly in the core types like LocalDate and LocalTime.

The nameof operator

This was a no-brainer in Noda Time. We have a lot of code like this:

public void Foo([NotNull] string x)
{
    Preconditions.CheckNotNull(x, "x");
    ...
}

This trivially becomes:

public void Foo([NotNull] string x)
{
    Preconditions.CheckNotNull(x, nameof(x));
    ...
}

Checking that every call to Preconditions.CheckNotNull (and CheckArgument etc) uses nameof and that the name is indeed the name of a parameter is going to be one of the first code diagnostics I write in Roslyn, when I finally get round to it. (That will hopefully be very soon – I’m talking about it at CodeMash in a month!)

Dictionary initializers

We’ve been able to use collection initializers with dictionaries since C# 3, using the Add method. C# 6 adds the ability to use the indexer too, which leads to code which looks more like normal dictionary access. As an example, I’ve changed a “field enum value to delegate” dictionary in TzdbStreamData from

private static readonly Dictionary<TzdbStreamFieldId, Action<Builder, TzdbStreamField>> FieldHanders =
    new Dictionary<TzdbStreamFieldId, Action<Builder, TzdbStreamField>>
{
   { TzdbStreamFieldId.StringPool, (builder, field) => builder.HandleStringPoolField(field) },
   { TzdbStreamFieldId.TimeZone, (builder, field) => builder.HandleZoneField(field) },
   { TzdbStreamFieldId.TzdbIdMap, (builder, field) => builder.HandleTzdbIdMapField(field) },
   ...
}

to:

private static readonly Dictionary<TzdbStreamFieldId, Action<Builder, TzdbStreamField>> FieldHanders =
    new Dictionary<TzdbStreamFieldId, Action<Builder, TzdbStreamField>>
{
    [TzdbStreamFieldId.StringPool] = (builder, field) => builder.HandleStringPoolField(field),
    [TzdbStreamFieldId.TimeZone] = (builder, field) => builder.HandleZoneField(field),
    [TzdbStreamFieldId.TzdbIdMap] = (builder, field) => builder.HandleTzdbIdMapField(field),
...
}

One downside of this is that the initializer will now not throw an exception if the same key is specified twice. So whereas the bug in this code is obvious immediately:

var dictionary = new Dictionary<string, string>
{
    { "a", "b" },
    { "a", "c" }
};

if you convert it to similar code using the indexer:

var dictionary = new Dictionary<string, string
{
    ["a"] = "b",
    ["a"] = "c",
};

… you end up with a dictionary which only has a single value.

To be honest, I’m now pretty used to the syntax which uses Add – so even though there are some other dictionaries initialized with collection initializers in Noda Time, I don’t think I’ll be changing them.

Using static members

For a while I didn’t think I was going to use this much – and then I remembered NodaConstants. The majority of the constants here are things like MillisecondsPerHour, and they’re used a lot in some of the core types like Duration. The ability to add a using directive for a whole type, which imports all the members of that type, allows code like this:

public int Seconds =>
    unchecked((int) ((NanosecondOfDay / NodaConstants.NanosecondsPerSecond) % NodaConstants.SecondsPerMinute));

to become:

using NodaTime.NodaConstants;
...
public int Seconds =>
    unchecked((int) ((NanosecondOfDay / NanosecondsPerSecond) % SecondsPerMinute));

Expect to see this to be used a lot in trigonometry code, making all those calls to Math.Cos, Math.Sin etc a lot more readable.

Another benefit of this syntax is to allow extension methods to be imported just from an individual type instead of from a whole namespace. In Noda Time 2.0, I’m introducing a NodaTime.Extensions namespace with extensions to various BCL types (to allow more fluent conversions such as DateTimeOffset.ToOffsetDateTime()) – I suspect that not everyone will want all of these extensions to be available all the time, so the ability to import selectively is very welcome.

String interpolation

We don’t use the system default culture much in Noda Time, which the string interpolation feature always does, so string interpolation isn’t terribly useful – but there are a few cases where it’s handy.

For example, consider this code:

throw new KeyNotFoundException(
    string.Format("No calendar system for ID {0} exists", id));

With C# 6 in the VS2015 preview, this has become

throw new KeyNotFoundException("No calendar system for ID \{id} exists");

Note that the syntax of this feature is not finalized yet – I expect to have to change this for the final release to:

throw new KeyNotFoundException($"No calendar system for ID {id} exists");

It’s always worth considering places where a feature could be used, but probably shouldn’t. ZoneInterval is one such place. Its ToString() feature looks like this:

public override string ToString() =>
    string.Format("{0}: [{1}, {2}) {3} ({4})",
        Name,
        HasStart ? Start.ToString() : "StartOfTime",
        HasEnd ? End.ToString() : "EndOfTime",
        WallOffset,
        Savings);

I tried using string interpolation here, but it ended up being pretty horrible:

  • String literals within string literals look very odd
  • An interpolated string literal has to be on a single line, which ended up being very long
  • The fact that two of the arguments use the conditional operator makes them harder to read as part of interpolation

Basically, I can see string interpolation being great for “simple” interpolation with no significant logic, but less helpful for code like this.

Null propagation

Amazingly, I’ve only found a single place to use null propagation in Noda Time so far. As a lot of the types are value types, we don’t do a lot of null checking – and when we do, it’s typically to throw an exception if the value is null. However, this is the one place I’ve found so far, in BclDateTimeZone.ForSystemDefault. The original code is:

if (currentSystemDefault == null || currentSystemDefault.OriginalZone != local)

With null propagation we can handle “we don’t have a cached system default” and “the cached system default is for the wrong time zone” with a single expression:

if (currentSystemDefault?.OriginalZone != local)

(Note that local will never be null, or this transformation wouldn’t be valid.)

There may well be a few other places this could be used, and I’m certain it’ll be really useful in a lot of other code – it’s just that the Noda Time codebase doesn’t contain much of the kind of code this feature is targeted at.

Conclusion

What a lot of features! C# 6 is definitely a “lots of little features” release rather than the “single big feature” releases we’ve seen with C# 4 (dynamic) and C# 5 (async). Even C# 3 had a lot of little features which all served the common purpose of LINQ. If you had to put a single theme around C# 6, it would probably be making existing code more concise – it’s the sort of feature set that really lends itself to this “refactor the whole codebase to tidy it up” approach.

I’ve been very pleasantly surprised by how much I like expression-bodied members, and read-only automatically implemented properties are a win too (even though I need to get used to it a bit). Other features such as static imports are definitely welcome to remove some of the drudgery of constants and provide finer-grained extension method discovery.

Altogether, I’m really pleased with how C# 6 has come together – I’m looking forward to merging the C# 6 branch into the main Noda Time code base as soon as I’ve got my continuous integration server ready for it…

44 thoughts on “C# 6 in action”

  1. Thanks for the real-world examples. New language features can sometimes be hard to understand when only described in the abstract, and even code examples are not always the most useful, having a more contrived feel to them. But when you look at the features in the context of a library-wide application, it really brings things into focus.

    One thing I’m curious about: “Using static members”, I’m surprised I haven’t seen mention of an aliasing version of this new feature. I.e. it would be unwieldy to alias every static member of a type, and some static members are obviously from a given type. But there are other scenarios where you want to bring in just one or a handful of members, and the names are fairly non-specific. It would be nice to have the same sort of control over the balance of verbosity and clarity that the type-name aliasing does today.

    I guess that’s one of those “didn’t beat the hundred point starting deficit” things, where the benefit didn’t offset the added complication. Or maybe they were just focused on Java parity. Oh well…

    Like

  2. I’m rather sad the primary constructors didn’t make it, but your experiences list roughly what I expected.

    I’d love to hear if null propagation is actually as useful as it sounds. As you say for Noda, I expect that most of the time I want to handle null explicitly in my code, and regard things like x == null ? null : x.y == null ? null : x.y.z to be a smell in the first place. Making this a build in discourages me from thinking why a value could be null. I think this is a feature-to-avoid at least for the above scenario, leaving the more exotic scenarios like your use in Noda; those while useful aren’t sufficient to include in the language IMO. One of those things I’d love to be proven wrong about.

    Like

  3. I’m a little disappointed to read you only found a single place to refactor your code to use the much touted null propagation feature. I’m viewing this as a big sell, especially in the type of code I write that uses lots of third party API’s where null is frequently and unpredictably returned.

    Like

    1. That’s more about the nature of Noda Time code than anything else. Noda Time is an “odd” code base in various ways – it’s got a lot of custom value types, which is unusual to start with.

      Like

  4. We don’t use the system default culture much in Noda Time, which the string interpolation feature always does, so string interpolation isn’t terribly useful

    In the final version, a string interpolation expression will be convertible to IFormattable, so it will be easy to write a method to format it using the invariant culture: Invariant($"No calendar system for ID {id} exists").

    Like

        1. Excellent, thanks. Will have a look and see what that enables in Noda Time – although it may not be viable until the next preview version is available with $”” insteadof “\{…}”.

          Like

  5. C#: public int Year => date.Year;
    F#: public Year -> date.Year

    the languages are really converging, no? Are you going to update your Functional Programming book with Tomas?

    Like

  6. I feel like the new indexing dictionary initializer not throwing an exception for a duplicate key is not the correct behavior. I understand that it’s functionally equivalent to initializing the dictionary and manually adding items using the indexer, but in most (if not all) instances when using the initializer you’ll specify unique keys…

    Do you think this is something they would consider changing?

    Like

    1. While I take your point, I think that would be a bit misleading – or even impossible. Given that you’re using indexer syntax, it makes sense to call the indexer… and if the indexer you happen to call (which could be on any type – not just a dictionary) doesn’t throw an exception, what can the compiler do about it?

      Basically, if you want Add to be called, keep using the existing syntax – if you want the indexer to be called, use the new syntax. I’ll probably keep using the existing syntax in most cases.

      Like

      1. I guess I agree… I’m probably just used to the existing Add syntax. That, however, brings up another question in my mind – in what situations is this new indexer syntax really useful? Does the indexer perform much faster than the Add syntax? I wouldn’t imagine it would.

        I can’t think of any other scenarios I’d use the new syntax… the code isn’t any cleaner using it.

        Like

        1. How about a situation where there’s an indexer but no (viable) add method? I can see that being very feasible for a wrapper type around an array, for example – a fixed-length list, or maybe a bit vector.

          Like

  7. I recently read that on the job you write Java. If that’s true, isn’t that a bit like Rodin spending his days painting, or Van Gogh sculpting the day away?

    Like

  8. Hey Jon,

    Great blog post (as always). I’m interested that you mentioned that you are speaking at CodeMash? I’m sure I read a short while ago that you were not doing any more speaking engagements so that you could finish your book. I recently saw your talk at NDC London and really enjoyed it.

    Kevin

    Like

    1. I’m not accepting any more new speaking engagements – I’ve had CodeMash in the diary for a long time :) Likewise I’m going to be talking at NorDevCon in February, but that’s all that’s on my calendar now…

      Like

  9. Have you skipped some features? I have been reading about primary constructors, a new pattern-matching operator, and some odd dict.$key syntax. If primary constructors were dropped, I don’t mind, since I didn’t like the way they were planned to work anyway, for reasons you have already discussed yourself.

    See also, C# 6 vs EC#: http://loyc.net/2014/cs-6-versus-ecs.html

    My version of the “primary constructor” looks like this:

    public class Patent
    {
    public Patent(
    private string _title,
    private string _yearOfPublication
    ) {}
    }

    This is not a “primary” constructor, it is just a combination of variables declarations and a normal constructor. It may look ugly at first, but combining declarations in this way is highly flexible: you can decide whether to create fields or properties, the constructor can contain validation code, it can be public/private/protected as usual. The feature is not even tied to constructors; you can use it with normal methods, and instead of writing “private string” you could write “set string” to set an existing variable or property instead of creating a new one.

    This feature works today as part of LeMP (Lexical Macro Processor), which my grow into a proper compiler later. It consists of an “Enhanced C#” parser and a set of macros, including one macro that converts the above constructor into

    private string _title;
    private string _yearOfPublication;
    public Patent(string title, string yearOfPublication)
    { _yearOfPublication = yearOfPublication; _title = title; }

    I’ve been meaning to finish the initial set of macros and publish an article on CodeProject, but I’ve been stumped for months about certain design elements and with no collaborators, I have no one else to give me ideas. Anyway, the hope is that people currently using T4 for C# code generation will switch to LeMP, as it allows you to write cleaner code.

    Like

    1. I’ve skipped a very few – using an extension Add method for collection initializers springs to mind but primary constructors and dict.$key were definitely dropped, along with declaration expressions and numeric literal enhancements mentioned in your blog. I’m not sure that pattern matching was ever actually planned for C# 6, but it’s definitely still in some heads for a future version…

      Like

  10. The numeric literal enhancements were dropped, what? It’s such a trivial thing, I don’t get why they would drop it. I know they had a “point” system where a feature had to have a certain amount of value to be included. But really it should be a ratio: ratio of implementation/documentation/maintenance effort versus value. Underscores like 123_456_789 in particular are literally just one line of code changed in the lexer. Binary literals shouldn’t be much harder. And since many other languages already include those two exact features in exactly the same form… why drop it? I’m also sad to hear about variable declaration expressions being dropped. Mathematicians have had that feature in their language for more a hundred years (“If the determinant d is greater than sigma, …” => if (determinant()::d > sigma)…)

    Like

  11. Regarding using static members … when I switched from VB.NET to C# I had to get used to specifying Math.Sqrt(x) instead of Sqrt(x). I soon preferred the C# way. What I can see happening now is someone can create a static class named “Global” with a capital “G” and instead of using static methods like Global.Foo(), where its clear to see Foo() is a member of the Global class that they will simply use Foo(). I’m not super thrilled about that.

    I am thrilled to see NodaTime become more mature. I work a lot with process data historians, particularly OSIsoft’s PI Data Archive, where every value of hundreds of millions is timestamped in Utc. Its a challenge to write an app where a user someone in the world access a server in U.S. Central Standard Time but for data that may be in Ireland. When someone says “I want to see yesterday’s data” you figure they mean presumably at midnight but FOR WHICH TIMEZONE? The user’s local time zone, or the remote app server, or the PI server, or for the plant where the data comes from? I hoping NodaTime may be a good fit for some of our apps.

    Like

  12. Nice post, Jon. Very good explanation of the new features. However, I have to ask: am I the only one who thinks that a lot of these features, while making code more succinct, make the syntax less C#-y (the equal sign after the curly braces of an auto property, for example)?

    It seems to me that these features could have been implemented with better syntax. The nameof operator seems genuinely useful (especially in WPF), but the rest seems like unnecessary compiler candy.

    Like

  13. Nice Post, I think they added to much unneeded function and syntax to C#. If we keep on going this way it will make it more harder to read and understand. “public static LocalDateTime Add(LocalDateTime localDateTime, Period period) =>
    localDateTime.Plus(period);” It total unneeded.

    Like

  14. I really like how I can create methods for firing events with a single line.
    For example: protected void OnPropertiesChanged(string name) => PropertiesChanged?.Invoke(this,name);

    Like

Leave a comment