It’s been a scandalously long time since I’ve blogged about C#, and now that the first C# 6 preview bits are available, that feels like exactly the right thing to set the keys clacking again. Don’t expect anything massively insightful from me just yet; I’d heard Mads and Dustin (individually) talk about some new features of C# 6 at conferences, but this is the first opportunity I’ve had to play with the bits. There are more features to come, and I suspect that the ones we’ve got aren’t in quite the shape they’ll be in the end.
First up, if you haven’t been following Build, here are some of the resources to get you going:
Firstly, the fact that Roslyn is now open source (under the Apache 2 licence, no less) – this is incredible news. I gather it’s already working under Mono (not sure whether that’s in its entirety, or just the most important parts) which can only be a good thing. I don’t know whether I’ll have the time and energy to fork the code and try to implement any little features of my own, but it’s definitely nice to know that I could. I’ll definitely try to poke around the source code to learn good patterns for working with immutability in C#, if nothing else.
I’m not going to list the C# 6 features and go through them – read the docs that come with Roslyn for that. This is more in the way of commentary and early feedback.
Initializers for automatically implemented properties and primary constructors
I’ll talk about these two together because they’re so clearly related… which is part my beef with them. Let’s start out with the positive side: for very simple cases, these really will be wonderful. For times where you’re really just trying to wrap up a bunch of values with no additional validation, it’s really great. For example, in Noda Time I have a struct that currently looks like this:
internal struct Transition : IEquatable<Transition>
{
private readonly Instant instant;
private readonly Offset oldOffset;
private readonly Offset newOffset;
public Offset NewOffset { get { return newOffset; } }
public Instant Instant { get { return instant; } }
public Transition(Instant instant, Offset oldOffset, Offset newOffset)
{
this.instant = instant;
this.oldOffset = oldOffset;
this.newOffset = newOffset;
}
}
(In case you’re wondering: no, there’s no reason for the constructor and properties to be public within an internal struct. I’ll fix that shortly :)
In C# 6, that would be as simple as:
internal struct Transition(Instant instant,
private Offset oldOffset, Offset newOffset) : IEquatable<Transition>
{
public Instant Instant { get; } = instant;
public Offset NewOffset { get; } = newOffset;
}
This example, entirely coincidentally, shows how you can generate both fields and properties from constructor parameters.
Yay for read-only automatically implemented properties (although at-declaration initialization isn’t just restricted to read-only ones), yay for read-only fields without boilerplate constructor code, and all is generally goodness. Except… a lot of my types don’t end up looking like that. There are three main issues – validation, accessibility, and structs. Validation is already mentioned in the release notes as a potential cause for concern, and that one actually can be ameliorated to some extent. Accessibility is the assumption – as far as I can see – that the primary constructor should have the same accessibility as the class itself. Struct initialization is a relatively rare issue, but one worth considering.
LocalDateTime in Noda Time highlights all three of these issues in one handy parcel. Here’s some of the code:
public struct LocalDateTime
{
private readonly CalendarSystem calendar;
private readonly LocalInstant localInstant;
internal LocalInstant LocalInstant { get { return localInstant; } }
public CalendarSystem Calendar
{
get { return calendar ?? CalendarSystem.Iso; }
}
internal LocalDateTime(LocalInstant localInstant, CalendarSystem calendar)
{
Preconditions.CheckNotNull(calendar, "calendar");
this.localInstant = localInstant;
this.calendar = calendar;
}
}
In C# 6, I could *sort of* achieve a similar result, like this:
public struct LocalDateTime(LocalInstant localInstant, CalendarSystem calendar)
{
private CalendarSystem Calendar { get; } = Preconditions.CheckNotNull(calendar,
"calendar");
internal LocalInstant LocalInstant { get; } = localInstant;
}
I’ve worked around the validation, by putting it in the property initialization. That’s not too bad, but it does potentially mean that your previously-centralized validation is scattered around the code a bit more – it lacks clarity.
We now have a public constructor instead of an internal one – which in this case wouldn’t work for me at all, as LocalInstant is an internal type. In fact, LocalDateTime does have some public constructors, but quite often I create types with no public constructors at all, just private constructors and static factory methods. Unless I’ve missed some way of separating the accessibility of the constructor from the accessibility of the type, primary constructors simply won’t be applicable for those types.
Finally, the struct part. Note how the Calendar property in the original code uses the null-coalescing operator. That’s because values of structs can always be created without going through a constructor, making the validation less helpful than it would otherwise be. In Noda Time I’ve attempted to make the default values for all structs act as if they’re valid values, even if they’re not useful values. (ZonedDateTime uses a time zone of UTC in a similar fashion.) I suspect there’s no equivalent for this using read-only automatically implemented properties – but the fix here would probably be to use an "auto read-only private field" and back that with a property – not too much of a hardship.
I think what bothers me most about this pair of features is how tightly coupled they are. If you don’t want to have a primary constructor (because you want to have more logic than it allows, or because you want to change the accessibility), but still want to initialize read-only properties, you’re forced back to declaring a separate private backing field. I think it would actually be reasonable to treat read-only properties like read-only fields, and allow them to be initialized from normal constructors too. I doubt that I’ll prevail, but I’ll certainly make the point to the team. (Heck, this may be the thing I try to implement in a Roslyn fork, just for kicks. How hard can it be? ;)
Again, I want to reiterate that in simple cases – even including where there’s simple validation, using helper methods – these features will be absolutely wonderful. I’d just like to be able to use them more often than it currently looks like I’d be able to.
Using static
Hooray! We’ll be able to select extension methods from a single type instead of from a whole namespace, like I asked for in 2005. I’m not the only one who’s been badgering the team on this for a while, so it’s great to see that it’s made it. I’m not 100% keen on the fact that it looks like any other using directive – I think it would help with clarify if it were "using static …" instead, but I’m not going to beef about that. (EDIT: I see from the language design notes that this option was considered as recently as February 2014.)
Note that this will further simplify the validation in the example above – I can see myself adding a using directive for Preconditions very frequently. It will also mean that I might get round to adding a load of extension methods into NodaTime.Testing – I didn’t want to introduce one namespace per kind of extension method, but this will be much cleaner. (I’ll still use a separate namespace to shield any pre-C#-6 users from seeing a slew of them, mind you.)
Declaration expressions
The language guide has this to say near the end:
Declaration expressions take a little getting used to, and can probably be abused in new and interesting ways.
I’m going to reserve judgement here. I’m instinctively against them as I’m sure they’ll encourage out parameter usage… but I suspect that’s the point – that out parameters aren’t too bad when they’re easy to use. It still feels like a little bit of a workaround – the language is still really designed to return a single value, and out parameters help you out when you’ve got some good reason to want to return more than one value. The big question is whether returning more than one value is a good thing or not. If it’s not – if it should only be done in extremis – then we shouldn’t be making it easier. If it is a good thing, might there not be better ways of making it easier? Maybe not – aside from anything else, this is a relatively small change, compared with alternatives. It seems likely that a platform/language designed with multiple return values in mind would not use the same approach, but that ship has sailed.
The beneficial side effect is the cleaner way of using the "as" operator to both assign and test in one go, as per the example in the guide:
if ((string s = o as string) != null) { … }
While this clearly lack the joyous vulgarity of my alternative workaround:
for (string s = o as string; s != null; s = null) { … }
… I have to grudgingly say I prefer it overall.
I’m not quite egocentric enough to think that Mads specifically had me in mind with the phrase "and can probably be abused in new and interesting ways" but I’ll obviously do my best to find something.
Anything else?
The other features (exception filters, binary literals, separators in literals, indexed members and element initializers, await in catch and finally blocks, and extension Add methods) all seem fairly reasonable to me – or at least, I don’t think I have anything interesting to say about them yet. I’m hopeful that exception filters could create some really interesting mayhem due to the timing of when the condition is evaluated, but I haven’t got as far as abusing it in a genuinely glorious way yet.
Conclusion
I’m really, really looking forward to C# 6. Despite my reservations about primary constructors and read-only automatically implemented properties, they’re still my favourite features – and ones which I hope can be made more widely useful before the final release.
The question I naturally ask myself is, "Where will this make Noda Time cleaner?" (Bear in mind that I’m still just a hobbyist in C# – Noda Time is as close to a "production" codebase as I have.) I can certainly see myself updating Noda Time to require C# 6 to build (but keeping the .NET 3.5 library requirement) fairly soon after the final release – there’s plenty that will make life simpler… and more to come, I hope.
So many thanks, C# team – and I’ll look forward to the next preview!