In Part 1 and Part 2 we saw how C# formed and evolved along the years, from its inception in 2000 up to C# 6.0 release in 2015. Initially, C# was born as a strictly typed object-oriented language. It was influenced somewhat by Java and Delphi and was even called a Java imitation by James Gosling, the creator of the Java.

In later versions, C# evolved and adopted some functional programming attributes with Lambda Expressions, LINQ and Extension Methods. C# gained incredible traction and popularity with those features. After C# 3.0 in 2008, it became the 7th most popular language according to the TIOBE index.

Going still further in the time, C# imitated F# asynchronous workflows and created the async/await feature. C# also improved COM interoperability catching up to Visual Basic .NET, which was the COM reigning king up to that time. After C# 4.0 in 2010, its popularity rises and it becomes the 4th most popular language in the world according to the TIOBE index.

We saw that while C# imitated most of its features from existing languages (as would be expected from a programming language), it does so very well, often better than the original innovator. C# had some incredible innovations as well like LINQ.

In this article, we will see C# evolving towards a completely different area than the previous versions, and trying to become the one language that ruled them all.

C# 7.0

C# 7.0 is released in March 2017. By now, the new Roslyn compiler is alive and kicking, which allows the C# team to release many minor features quickly.

Tuples – Tuples were available before C# 7, but they had no language support. Each item was referenced as Item1 , Item2 , etc. C# 7.0 adds a very convenient syntax and language support to Tuples:

1 ( string Alpha , string Beta ) namedLetters = ( "a" , "b" ) ; 1 2 3 4 5 private static ( int celsius , int fahrenheit ) GetTemperature ( ) { . . . return ( c , f ) ; } already existed in Python and in Scala. Imitation

NOTE: When I call something ‘Imitation’, I will show at least one example of a programming language with the same feature existing before C#. But I don’t mean to claim that language is the original source of the feature.

out variables – We can now declare the out variable inline in the method.

1 2 3 4 5 6 // before int number1 ; int . TryParse ( input , out number1 ) ) // now int . TryParse ( input , out int number2 ) )

The out parameter is by itself unique to C#, so there’s no verdict on this one.

variables – We can now declare the variable inline in the method. The parameter is by itself unique to C#, so there’s no verdict on this one. Discards – You can use the special underscore _ character for variables that you won’t need later. Useful when deconstructing tuples and when using out parameters.



1 2 //returns name, license info, etc. but we care just about bithday and address var ( _ , _ , birthday , _ , address ) = GetCurrentUserDetails ( ) ; used in Python, in Scala, and there’s a similar functionality in Perl . Imitation

character for variables that you won’t need later. Useful when deconstructing tuples and when using used in Python, in Scala, and there’s a similar functionality in . Pattern Matching – The code speaks for itself:

1 2 3 4 5 6 7 8 9 10 11 12 13 // 'is' type pattern if ( shape is Square square ) return square . Side * square . Side ; // in 'switch' statements switch ( shape ) { case Square square : return square . Side * square . Side ; // 'when' clauses in 'case' expressions switch ( shape ) { case Square sqr when sqr . Side == 0 :

As far as innovation, Scala and Kotlin already have similar pattern matching and C# is playing catchup here. Imitation

NOTE: This article is not meant to be judgmental. I do not mean to say that imitation is a bad thing. Both imitations and innovations are necessary to create a great language.

ref locals and returns – Allows references to variables, much like pointers in C.

1 2 3 4 5 6 int [ , ] matrix = . . . . . . ref var item = ref MatrixSearch . Find ( matrix , ( val ) = > val == 42 ) ; //assuming the cell found is (4,2) item = 123 ; Console . WriteLine ( matrix [ 4 , 2 ] ) ; //prints '123' Innovation

locals and returns – Allows references to variables, much like pointers in C. Local Functions – Nesting functions inside other functions to limit their scope and visibility.

1 2 3 4 5 6 7 8 9 10 11 12 public static void ShowXTimes ( string str , int x ) { for ( int i = 0 ; i < x ; i ++ ) { Show ( x ) } void Show ( ) { Console . WriteLine ( str ) ; } } Python and JavaScript. Imitation

Python and JavaScript. More expression-bodied members – A new syntax for methods and properties:

1 2 3 4 5 6 7 8 9 10 // Expression-bodied constructor public ExpressionMembersExample ( string label ) = > this . Label = label ; private string label ; // Expression-bodied get / set accessors. public string Label { get = > label ; set = > this . label = value ? ? "Default label" ; }

throw Expressions – You can throw exceptions in conditional expressions.

1 2 private ConfigResource loadedConfig = LoadConfigResourceOrDefault ( ) ? ? throw new InvalidOperationException ( "Could not load config" ) ; if-then-else which acts as an expression. So you can write in a similar fashion:

1 2 F #: let res = if ( y = 0 ) then failwith "Divisor cannot be zero." else x / y Imitation Thanks to Reddit user AngularBeginner for the correction

Expressions – You can throw exceptions in conditional expressions. which acts as an expression. So you can write in a similar fashion: Generalized async return types – Methods declared with the async modifier can return other types in addition to Task and Task<T> like ValueTask<T> . Another improvement to an existing C# feature implementation.

modifier can return other types in addition to and like . Another improvement to an existing C# feature implementation. Numeric literal syntax improvements – Binary number 0b prefix, and _ digit separator for long numbers.

1 public const int Sixteen = 0b0001_0000 ; Python: 0b prefix, digit separator. Imitation

C# 7 is playing some catchup here with Tuples and Pattern Matching. These create extremely nice syntax and its part of the reason other languages like Python and Kotlin keep getting popular. Better late than never I say.

The ref locals feature is the beginning of a new strategy for C#, which will see more of later on. It allows more low-level control with the language, which in turn allows improving performance in algorithms and bottleneck pieces of code.

C# 7.1

In August 2017 C# 7.1 is released. It’s the first version which is not a round number. The language team seems to have decided to release smaller versions, but faster.

With 7.1 you can now configure the compiler to match a specific version.

async Main method – The entry point for an application can have the async modifier.

This is an Innovation and was later imitated by Python with async def main() and Kotlin with fun main() = runBlocking<Unit> {

NOTE: The research in this article, though thorough and long on my part, is not academic. I can make mistakes, so please comment or email me if a mistake is found and I’ll correct it.

default literal expressions – You can use default literal expressions with the default keyword when the target type can be inferred.

1 2 3 4 //before C# 7.1 int x = default ( int ) ; //with c# 7.1 int x = default ;

literal expressions – You can use default literal expressions with the keyword when the target type can be inferred. Inferred tuple element names – The names of tuple elements can be inferred from tuple initialization.

1 2 3 4 5 6 int count = 5 ; string label = "Colors used in the map" ; // before c# 7.1 var pair = ( count : count , label : label ) ; // with c# 7.1 var pair = ( count , label ) ; as of ES2015.

Not much to say about this version. Nothing revolutionary, mostly some sugar syntax features. It’s nice to see versions being released so quickly.

C# 7.2

Version 7.2 is released in November 2017, after only 3 months after the last release.

Span<T> and Memory<T> – Holds a pointer to a range of an array (part of it or all of it). So if you have a byte array of 1..50, then you can have a Span<byte> pointing to range 10..20.

Python’s slicing notation is very similar to this feature and in fact, more powerful. Imitation

Looking at Python’s capabilities comes with a nice promise of what Span<T> can become in future versions.

Even though it’s an imitation, it’s impressing that the C# team was able to achieve this with all the existing language use cases and limitations.

of an array (part of it or all of it). So if you have a array of 1..50, then you can have a pointing to range 10..20. Python’s slicing notation is very similar to this feature and in fact, more powerful. Looking at capabilities comes with a nice promise of what can become in future versions. Even though it’s an imitation, it’s impressing that the C# team was able to achieve this with all the existing language use cases and limitations. Techniques for writing safe efficient code – A combination of syntax improvements that enable working with value types using reference semantics. This includes: The in modifier on parameters, to specify that an argument is passed by reference but not modified by the called method. The ref readonly readonly modifier on method returns, to indicate that a method returns its value by reference but doesn’t allow writes to that object. The readonly struct declaration, to indicate that a struct is immutable and should be passed as an in parameter to its member methods. The ref struct declaration, to indicate that a struct type accesses managed memory directly and must always be stack allocated.The first 3 features seem to be inspired by the various const modifiers in C++. Imitation

However, the last item ref struct is an Innovation .

Non-trailing named arguments – Named arguments can be followed by positional arguments.

1 2 PrintOrderDetails ( productName : "Red Mug" , 31 , "Gift Shop" ) ; // Note that 31 is used without specifying name Innovation

Leading underscores in numeric literals – Numeric literals can now have leading underscores before any printed digits int binaryValue = 0b_0101_0101; . A small nice-to-have syntax improvement.

. A small nice-to-have syntax improvement. private protected access modifier – The private protected access modifier enables access for derived classes in the same assembly. Java did have a private protected modifier (now obsolete) in Java 1.0, which was removed as of JDK 1.0.2 (the first stable version). The obsolete modifier was defined as follows: The meaning of private protected was to limit visibility strictly to subclasses (and remove package access). Innovation

C# continues its strategy to allow better performance by letting the programmer low-level control. This strategy is already paying off. Span<T> and Memory<T> are used in .NET Core internal libraries to significantly increase performance.

This is box title [jetpack_subscription_form subscribe_text=”If you like this article, join the mailing list and get updates on new ones” title=”SUBSCRIBE VIA EMAIL” subscribe_button=”GO”]

C# 7.3

This version really focuses on improving safe code performance. The entire concept of unsafe code in a garbage collected environment is a C# Innovation, and doesn’t exist in any other garbage-collected environment (see fixed statement). So there’s no reason to determine if the following is an innovation or imitation since it’s all unique to C# anyway.

The following enhancements were made to existing features:

You can test == and != with tuple types – Already existed in Python Imitation

and with tuple types – Already existed in Python You can use expression variables in more locations – Relevant to out arguments, which are unique to C#.

arguments, which are unique to C#. You may attach attributes to the backing field of auto-implemented properties – Here is an example:

1 2 [ field : SomeThingAboutFieldAttribute ] public int SomeProperty { get ; set ; } supports annotations for Methods and Variables.

supports annotations for Methods and Variables. Method resolution when arguments differ by in has been improved

has been improved Overload resolution now has fewer ambiguous cases.

It seems the entire 7.x C# versions are related to improving unsafe code, pointers, and low-level memory management. In other words, C# aims to become as efficient for algorithms as C and C++.

That’s a pretty bold goal, but theoretically, this can be achieved in specific bottleneck algorithmic blocks of code. C# can effectively disable the garbage collection by pinning variables, and use the new ref and stackalloc capabilities to work on the stack with pointers, just like native C++ code. Whether algorithms will start getting written in C# remains to be seen.

With version 7.3 we effectively covered all C# versions until today. Now, it’s time to see what will happen in the future. But first, let’s see how C# is doing in terms of popularity in 2018.

C# Popularity

In recent years, C# notably isn’t used much in startups. That role is mostly filled by Java, JavaScript, Ruby, and Python. Nevertheless, C# remains to be extremely popular in the industry. StackOverflow’s survey of 2018 places C# as the 4th most popular programming language (3rd if discarding SQL). .NET Core is the 3rd most popular framework after Node.js and AngularJS.

For the fifth year in a row, JavaScript was the most commonly used programming language, according to Stack Overflow survey pic.twitter.com/owjCst7MMP — Rafael Kapela (@kapela) 28 November 2018

The TIOBE index places C# in 6th place, right after Visual Basic .NET (yes, really). The PYPL index places C# in 4th place after Python, Java, and JavaScript.

C# 8.0

We are getting very close to C# 8.0 release, which is said to come out with the Visual Studio 2019 preview by year’s end.

Mads Torgersen, the Program Manager of C#, recently wrote about all the new features in C# 8. Let’s go over them and see which are innovations and which are imitations:

Nullable reference types – All our reference types, nullable by default will now show a compiler Warning when assigned null:

1 2 string s = null ; // Warning: Assignment of null to non-nullable reference type string ? s = null ; // Ok Billion Dollar Mistake.

So as not to hurt existing code, this setting is configurable. Typescript implemented a similar feature with strict nullable types. Imitation



Billion Dollar Mistake. So as not to hurt existing code, this setting is configurable. Async Streams – Allows to await foreach on asynchronous methods and yield return results: 1 2 3 4 5 6 7 async IAsyncEnumerable < int > GetBigResultsAsync ( ) { await foreach ( var result in GetResultsAsync ( ) ) { if ( result > 20 ) yield return result ; } } I figure it’s like BlockingCollection’s GetConsumingEnumerable for async methods? I’ll have to ask Mads myself… But if I figured right it’s an Innovation

– Allows to on asynchronous methods and results: Ranges and Indices – Adds the Index type which can act as an index in arrays: 1 2 3 4 Index i1 = 3 ; // number 3 from beginning Index i2 = ^ 4 ; // number 4 from end int [ ] a = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } ; Console . WriteLine ( $ "{a[i1]}, {a[i2]}" ) ; // "3, 6" and a new Range syntax, whose type is undecided but will probably be Span<T> 1 var slice = a [ i1 . . i2 ] ; // { 3, 4, 5 } Both of those features exist in a similar fashion in Python with negative indexes and slice notation.

– Adds the Index type which can act as an index in arrays: Default implementations of interface members – Kind of like in abstract classes, an interface can provide a default implementation which the implementing class can choose to override or not. After a very long while, C# imitates a feature that first appeared in Java. Imitation

This raises questions about the difference between abstract classes and interfaces and also about problems with multiple-inheritance. This Java article refers to some of those questions and it’s also relevant to C#.

– Kind of like in abstract classes, an interface can provide a default implementation which the implementing class can choose to override or not. After a very long while, C# imitates a feature that first appeared in Java. This raises questions about the difference between abstract classes and interfaces and also about problems with multiple-inheritance. This Java article refers to some of those questions and it’s also relevant to C#. Recursive patterns – This is a really interesting progress in pattern matching: 1 2 3 4 5 6 7 IEnumerable < string > GetEnrollees ( ) { foreach ( var p in People ) { if ( p is Student { Graduated : false , Name : string name } ) yield return name ; } } In this example, if p is Student and p.Graduated is false and Name is not null, then Name is returned.

If you’re like me you probably had a hard time to wrap your head around this syntax at first. In my opinion, it’s a bit more intuitive to write

if (p is Student st && !st.Graduated && st.Name!= null) yield return name; This recursive patterns already existed in Rust and in Erlang. Imitation

Thanks to Reddit user MEaster and to Daniel WM for the correction

Switch expressions – A new syntax for switch pattern matching: 1 2 3 4 5 6 7 var area = figure switch { Line _ = > 0 , Rectangle r = > r . Width * r . Height , Circle c = > Math . PI * c . Radius * c . Radius , _ = > throw new UnknownFigureException ( figure ) } ; It’s very similar to Kotlin pattern matching syntax.

– A new syntax for pattern matching: Target-typed new-expressions – When an object type can be derived from the expression, it’s allowed to be omitted: 1 Point [ ] ps = { new ( 1 , 4 ) , new ( 3 , - 2 ) , new ( 9 , 5 ) } ; // all Points An Innovation (for a statically typed language at least)

The big feature in C# 8 is the Nullable reference types. Hopefully, it will allow for safer code and much fewer Null-Reference-Exceptions.

The other features are mostly nice-to-have additions, and the Recursive patterns will probably take some getting used to…

Summary

This last article sums up all C# features from the first version release in 2002 up to C# 8 due to be released at the end of 2018. We also saw how C# gained traction along the way, remaining consistently one of the most popular languages in the world.

It’s quite incredible all the turns and changes C# took during all those years. I think it became one of the only truly multi-purpose languages in existence. Consider the following paradigms that exist in parallel in the C# language:

It’s object-oriented

It’s functional (LINQ, extension methods)

It’s managed (garbage collected)

It utilizes pointers and unsafe code like an unmanaged language

It’s statically typed

It can be dynamic (with the dynamic keyword)

From the comments I received, these articles were somewhat controversial. Some people thought I was trying to be judgemental, and there was no point in it. My point wasn’t to judge though. Rather I wanted to explore the history of the C# language and how it fared and evolved along the years in the context of other languages.

Anyway, I hope you got some benefit from the series, perhaps discovering a few C# features you didn’t know about (I sure did). I’d love to hear your feedback in the comments below and subscribe to the blog to get notified on new articles.

Share:

Enjoy the blog? I would love you to subscribe! Performance Optimizations in C#: 10 Best Practices (exclusive article) SUBSCRIBE