Freigeben über


C# 7.0 and Beyond

Guest blog by Microsoft Student Partner Henry Thompson

image
 
I’m an incoming third year studying Computer Science at Cambridge University and a Software Engineer Intern with Microsoft at their headquarters in Redmond, Washington. I love to write code as my hobby, and hope to focus on programming language semantics for my final year university project. I also enjoy heading up the sponsorship drive for the university’s annual hackathon, Hack Cambridge, and playing cello in my free time. More about me: https://henrythompson.me 

Introduction

Back in May, 50 MSPs from around the world were invited to attend Microsoft Build, the annual developer conference in Seattle where Microsoft unveils its new products and announces its plans for the forthcoming year. As one of those lucky enough to attend and be there as the newest developments at the company were announced, I would love to share with you some highlights, starting with the announcements from the C# team.

A Little Background

Over the past few years, Microsoft's change in their approach to their programming language ecosystem has been nothing short of dramatic. With .NET Core now spanning cross-platform on MacOS and Linux, and tools like Xamarin bringing .NET to Android and iOS, .NET and the languages that run on it (Visual Basic, C# and F#) are seeing deployment in an ever a broader range of scenarios. Whilst it was extremely exciting to see a renewed vigour and focus on F# at Build this year, C# remains by far .NET's most popular language. According to StackOverflow's 2017 survey it is the 4th most popular language in the world and for good reason – it is excellently designed for its targeted audience. A pragmatic and productive language, it still retains elegance and packs many sophisticated features which don't get in your way if you never need them.

For a language that is already thriving, it was exciting to be there at Build 2017 when the newest C# 7.0 features were discussed, C# 7.1 was announced and even sneak peeks of C# 8.0 were demonstrated (recording: https://channel9.msdn.com/Events/Build/2017/B8104 

These new features, whilst small individually, together add significantly more value yet to what C# offers. And as ever the tooling support that Microsoft provides around .NET languages, accelerated thanks to its Open Source efforts and projects such as Roselyn, form a key part of its developer offerings.

Focusing purely on the language design side of things and putting the developer tools to one side, here are just three highlights for C# discussed at Build this year which I felt indicated most clearly the overall direction of the language's design going forward.

Tuple Enhancements

C# 7.0 introduced a notable absence from C#'s arsenal of features: tuples. People can endlessly argue about whether these are a "good" feature – and certainly some people in the room were not totally pleased with their existence – but the C# team has added some elegant features to tuples in C# 7.1 which act as solutions to many of the criticisms.
For example, usually the meaning behind each element in a tuple is opaque – what does the first element mean? And the second? Unlike other similar imperative languages supporting tuples C#  associates names with each element. For example, we may declare

    1: public (int sum, int count) Tally(int[] values) 
    2: { var 
    3: result = (s: Sum(values), c: values.Length); 
    4:  return result; 
    5: } 

And when we consume a Tally return value we can use the declared names. Unlike in some other languages it is clear which value we are using!

    1: public void Example() { var tally = Tally(new int[]{1, 2, 3}); 
    2:  var tupleSum = tally.sum; 
    3:  // Equivalently var (tupleSum2, _) = 
    4: Tally(new int[]{1, 2, 3}); 
    5: } 

Note also that this is not a record type, and so it is fine to return the tuple r in the Tally method despite its element names s and c not agreeing with the method signature's names of sum and count. What matters is that their type of int × int (a pair of ints) is preserved and the names simply act as additional identifiers.

It is worth noting that these tuples are in fact value-types (underlying them are structs), so not only is it easier than creating your own custom object to encapsulate multiple return values, but it is also more efficient. They do talk about some of the controversy in that these tuples are essentially structs with public mutable fields, almost universally thought of as "bad" but as they explain C# tuples are not meant to be a thick abstraction but merely a way to pass multiple variables as one. For that reason and the intended usage of tuples, the simplicity makes absolute sense, yet another demonstration of C#'s attention to pragmatism.

As Mads Kirstensen from the Visual Studio team https://madskristensen.net/ says in the talk, these names are "just a guardrail" since, whilst they don't increase the expressiveness of C#, they improve its readability enormously. C# 7.1 will add a new feature which really highlights the attention to detail the C# team has: from C# 7.1 on, tuple element names will be implicitly inferred from the variables that initialise it.

For example, the following is creates a tuple whose first term has name x and second has term y:

var x = 0;

var y = 1;

var z = (x, y); // Instead of var z = (x: x, y: y);

This sort of attention to detail makes C# a clear, intuitive and powerful language to use. The team has addressed many other similar small painpoints in the language which, whilst small individually, add up to huge improvements. And since these work naturally with the deconstructors provided in the new pattern matching capabilities, we see how the C# team considers the language as a whole when producing individual features; and thus the language fits together exceptionally well. There is so much more that can be said for how tuples are implemented in C# 7.1 and 7.2 and the video has all the information.

Pattern Matching

Originally a key feature in functional languages, C# 7.0's addition of pattern matching is sure to increase productivity by improving the clarity and brevity of code, by avoiding tedious monolithic if-else blocks. Adding pattern matching follows a trend wherein features from functional languages, though often modified before being imported to other languages, are beginning to coincide with other language paradigms. It is great to see C# borrowing these features, especially as they become further established in other popular languages like Swift and Rust. Of course earlier languages like Scala have had it for a long time and it has been a tenet of purely functional languages since their inception.
Pattern matching in C# works via their improved switch-case statements. Until C# 6.0, switch-cases were limited to primitive types like ints and strings. C# 7.0 now allows for switch-cases on arbitrary objects, and even adds in condition checking besides just checking against constant values like null and integer constants. Here is an example:

    1: public void Example(object o) 
    2: { 
    3:    switch (o)    
    4:     {       
    5:         case null:          
    6:             // If o is null          
    7:             …          
    8:             break;       
    9:     case int i:          
   10:             // o is an integer. Declare an integer i and assign it o.          
   11:             …          
   12:             break;       
   13:     case string s where s.Length < 10:          
   14:             // If o is string of length < 10 assign it to variable s          
   15:             …          
   16:                 break;       
   17:           default:          
   18:             …          
   19:                 break;    
   20:     } 
   21: } 

Deconstruction follows a very interesting feature wherein a Decontructor method must be added either as a member of an extension method to any type. In C# 8.0 some other features typically seen in functional languages are also being suggested, including a record type which strongly resemble Scala case-classes:

    1: class Person(string firstName, string lastName);

And that is it for the declaration of a simple Person record – and again we see increased brevity, increased productivity, and less verbose boilerplate! 

Default Interface Implementations

Coming in C# 8.0 will be the ability to add a default implementation for methods in interfaces. From a purely language-design point of view is not something I was not pleased to see - after all, the value of an interface is that it is used to define nothing more than the signatures within an implemented type, to describe merely the contract between a component without implementation details. Whilst it is true that default interface methods resolve a number of breaking-change issues when modifying interface definitions, the ability to have a default implementation of some class is already possible using abstract classes. C# largely sidesteps the issue by supporting extension methods anyway.

However, the decision to include it in the language is a critical one, since the motivation is clear: it is for compatibility with Java and Swift, which both support the feature. Since Android apps run on Java and iOS apps on Swift, to have a Xamarin API which is a true wrapper of the native Android/iOS APIs requires it. And so we see how C# is now seeing a new emphasis on usage on mobile devices through Xamarin. No doubt this is a key part of Microsoft's long-term business strategy, wooing developers to their tools and ecosystem. It is clear that Xamarin – in fact, all .NET cross-platform efforts – have become a key driver in the whole Microsoft developer landscape and it will be exciting to see where the ecosystem goes as a result.

Wrapping Up

From what I hear and see, certainly amongst students, C# perhaps has a bit of an image problem. It is associated with enterprise software, and I hear many people who dismiss it as being the language of badly-written line-of-business software. In reality it is a very well thought-through and polished, and Microsoft demonstrated in its announcements at Build the keen attention to detail which has made C# the language it is. With the increasingly open and wide ecosystem of .NET Core and Xamarin I hope to see C# become far more recognised for its strengths as a language, and its adoption continue to grow. Of course C# is not the right language for everything, but where a statically-typed, object-oriented, and moreover pragmatic language is needed C# is a strong candidate. I was personally impressed with C# 7.1 and 8.0's new features, and it is fantastic to see the C# team clearly listening to user feedback and fixing genuine pain points. The rate of language evolution across .NET, in both C# and F# in particular, are impressive. Continuing in this fashion I hope that C# succeeds in growing its audience.