Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Experimenting with C# 6’s New Features

0.00/5 (No votes)
23 Jan 2016 1  
C# 6 has been available for a long time, by now! Many developers have written many guides to list the features. I have written this post to explain those features, and to explain whether they are actually new or are they just sugar coating to the compiler.

Introduction and Background

It has been a very long time, and trust me, I am fed up of “Cool new features of C# 6!” posts already. I mean, each post has the same things, same topics, same content and (usually!) the same code blocks to demonstrate the thing too. However, I never wanted to write the blog to describe new things in C#, but now I think I should explain the things that are done “under the hood” for C# 6’s code. I have been going through many posts and I already know what these new “cool” features are, so I just wanted to test them out and see when they work, how they work and when they are not going to work. Well definitely, that is something that people aren’t expecting by the C# architect team, however, let’s find them out. :-)

Also, it has already been very late for C# 6 to be discussed because C# 7 has already been into the discussions of the team, you may want to read more about what C# 7 team has on their list. C# 7 Work List of Features.

Anyways, none of them are the final versions, final statements, or “C# 7 must-haves”. They are just what they are trying to inject to C# as per the demand and ideas provided to them on GitHub and other networks that they are having.

Enough of C# 7 already, now you have already seen all of the features that were introduced in C# 6 and most of you have been using those features in your applications. But, did you try to use them in a way they were not intended to be used? I mean, have you tried to use them in a very “unknown” or “uncertain” manner?

Let me share what I think of those features. I would also love to hear what you think of them! :-)

Counting the Features of C# 6

Now let me show you a few things about these services in a way that you were not expecting or you never cared about! These features are definitely interesting, but, a few are useful in one way, and a few are not at all useful in one way. Depends on your project, your perspective and ideas. So let me start by the very famous (and my favorite C# 6) feature: String Interpolation.

String Interpolation

This feature is a great feature, personally saying, this is the best feature that I find on C# 6 features list. What I find easy while using “string interpolation” is, that I don’t have to use String.Format, I don’t have to concatenate the strings, I don’t have to use the StringBuilder at all. What I can do is that I can just simply write the string and it would include the variable data in the string. Now let me show you an example of multiple methods that can be used.

// Creating the private field
private static string Name = "Afzaal Ahmad Zeeshan";

// Then inside the Main function.

// 1. Using string concatenation.
Console.WriteLine("Hello, my name is: " + Name);

// 2. Using string format
Console.WriteLine(string.Format("Hello, my name is: {0}", Name));

// 3. Using string builders
StringBuilder builder = new StringBuilder();
builder.Append("Hello, my name is: ");
builder.Append(Name);
Console.WriteLine(builder.ToString());

Technically, their result is the same. They all say the same message, and how they do it, we can find that out using the Assembly language (or the MSIL code, if you are a fan of MSIL and not Assembly at the debug time), however, we do know that they present the same value on screen.

Screenshot (5950)

Figure 1: Same string rendered and generated, as a result of many ways.

Their IL benchmark would be different (because internally, they are three different ways). That is because, they themselves are different. Their IL generated would be like this:

IL_0000: nop 
IL_0001: ldstr "Hello, my name is: "
IL_0006: ldsfld UserQuery.Name
IL_000B: call System.String.Concat
IL_0010: call System.Console.WriteLine
IL_0015: nop 

IL_0016: ldstr "Hello, my name is: {0}"
IL_001B: ldsfld UserQuery.Name
IL_0020: call System.String.Format
IL_0025: call System.Console.WriteLine
IL_002A: nop 

IL_002B: newobj System.Text.StringBuilder..ctor
IL_0030: stloc.0 // builder
IL_0031: ldloc.0 // builder
IL_0032: ldstr "Hello, my name is: "
IL_0037: callvirt System.Text.StringBuilder.Append
IL_003C: pop 
IL_003D: ldloc.0 // builder
IL_003E: ldsfld UserQuery.Name
IL_0043: callvirt System.Text.StringBuilder.Append
IL_0048: pop 
IL_0049: ldloc.0 // builder
IL_004A: callvirt System.Object.ToString
IL_004F: call System.Console.WriteLine
IL_0054: nop 

IL_0055: ret

You can see, that (indirectly) they are all different and execute different functions. What happens, is a “syntactic sugar“. They are typically just functions that are executed and compiler allows you to write all of these simply.

Now coming to the new way of writing the strings with variable content. C# 6 introduces this new way of writing the same message!

Console.WriteLine($"Hello, my name is: {Name}");

/* Output:
 * Hello, my name is: Afzaal Ahmad Zeeshan
 */

The output of this is also the same… But, how C# 6 does that is a bit of trick now. To find that part out, we will again go back and read the MSIL generated.

IL_0000: nop 
IL_0001: ldstr "Hello, my name is: {0}"
IL_0006: ldsfld UserQuery.Name
IL_000B: call System.String.Format
IL_0010: call System.Console.WriteLine
IL_0015: nop 
IL_0016: ret

Um, watching at this MSIL, what do you say? :-) If you pay attention to the highlighted code, this resembles the code that we wrote using the String.Format() method. So what happens is that this is also a syntactic sugar. There is no addition of anything, it is just what the compiler does for you! Compiler simply takes away everything, and generates that previous version of code. Yet, in a very friendly and simple way. I like it.

Remarks and References

String interpolation has been adopted by many programming languages by now. Swift by Apple and JavaScript (ECMAScript) is also moving forward to templated strings. I personally feel, since string type data is mostly used, this is a great feature. For more, please read:

  1. String class
  2. String.Format()
  3. String interpolation

Conditional try…catch

Now, I have never used VB.NET, because I was always busy in C# itself but I still hear that VB.NET had this feature way before C# implemented it. The conditional trycatch (or as the team calls them, “Exception filters“) just allows you to handle the exception when a condition is met, I am not going to talk about what they are, instead, I will talk about how you can use them in your code. So basically, if you execute a function that is supposed to have a side-effect, and before handling any further errors, you want to find out if there was a side-effect or did the error occur before the side-effect. You would possibly be left with only one way to check that out!

try {
   // Some code that breaks the execution.
} catch (MyException me) {
   // Just a function to check if value was updated
   if(field.Updated()) {
      // Log the error and then re throw the exception
   }
} catch (Exception e) {
   // Re-perform the same for other exceptions.
}

As an example of this case, have a look at the following code:

private int field = 25;

public static void Main(string[] args)
{
    try {
        Console.WriteLine("Do you want to change the field?");
        if(Console.ReadLine() == "yes") { field = 34; }
 
        // Just throw the exception
        throw new Exception();
    } catch {
        if(fieldModified()) {
           // Log error, it was thrown after modifying the field. 
        }
        // Other code...
    }
}

public static bool fieldModified() {
   return (field == 25) ? true : false;
}

This try catch would allow us to check if our fields were modified, if not, then we can simply ignore as there were no side-effects.

Now what C# 6 allows you to do is to add a condition outside the block, so that you don’t even have to enter the block itself, and the condition would just check if it needs to go to this block, if not, then exception would continue to grow and later catch blocks would try their luck, finally to the end. Something like this:

try {
    Console.WriteLine("Do you want to change the field?");
    if(Console.ReadLine() == "yes") { field = 34; }
 
    // Just throw the exception
    throw new Exception();
} catch when (fieldModified()) {
    // Log the errors, reverse the transaction etc. 
}

So instead of writing that condition inside, we can write that condition in front of that catch block itself. Technically, both were OK, only this way we are leaving most of the work to the compiler and we are designing the code to be more readable, read it like “try .. this code .. catch when field modified .. continue“. This provides you with a simple way of reading the code. The IL gets some branches around the catch block so that code can jump from one location to another and so on.

Remarks and References

Technically, I won’t use this in any of my applications because I try to avoid transaction-like programming. But, if you are using a transaction-like programming, such as SQL queries, bank management systems, I would recommend that you use these conditions on your catches. They can be of great use when you are logging errors in transactions. The function used would determine if there were changes (or what-ever you want to do in that function!) and if that condition is true, error would be caught, logged (if code is provided) and then re-thrown (if code is provided).

For more:

  1. C# : How C# 6.0 Simplifies, Clarifies and Condenses Your Code

nameof Operator

Before saying at all, I want to confess that I have been one of those responders, who said, you cannot have the name of the variable! Compiler doesn’t know the name of the variables, they are just labels. Well, not anymore. C# 6 provides you with another “amazing” feature that lets you get the actual name of the variable, not just the value of that variable.

Before C# 6, nameof operator was not present. With C# 6, this new feature can allow you to know the names of the variables, not just their values. So for example, previously we did:

Console.WriteLine($"Hello, my name is {Name}");

Now we can do this too:

Console.WriteLine($"Value of {nameof(Name)} variable is {Name}");

Screenshot (6011)

Figure 2: Use of nameof operator in real world.

However, the actual use cannot be determined here. The actual use of this would be when we want to know the variable names too. We could definitely (before C# 6) hardcode the names, but after modification, refactoring, they would be of no use anymore. However, now C# 6 can allow us to have the names of those variables too.

To understand the working, have a look at the MSIL generated:

IL_0000: nop 
IL_0001: ldstr "Value of {0} variable is {1}"
IL_0006: ldstr "Name"
IL_000B: ldarg.0 
IL_000C: ldfld UserQuery.Name
IL_0011: call System.String.Format
IL_0016: call System.Console.WriteLine
IL_001B: nop 
IL_001C: ret

This shows that the variable names are passed as string objects to the function and then the string formatted (as we talked in the string interpolation), and finally the result is printed. So basically, all of this is just sugar-coating.

Null-conditional Operator

For this feature too, I have always been very narrow-minded. I never liked it, perhaps because I have written a “good way to handle null exception” as:

if(obj != null) {
   // "Safe" code here
}

// OR
try {
   // Code here...
} catch (NullReferenceException e) {
    // Code here...
}

But, things have changed. Previously, I was writing a library, in which I was supposed to get a list of objects from JSON data. So basically, if the source was empty or like “[]”, the array would be typically null. In other words, to overcome the exception, I would have to write something like this:

// Deserialize the string
var list = JsonConvert.DeserializeObject<List<Type>>(jsonString);

// Then check if it is null before returning
return (list == null) ? new List<Type>() : list;

//  Or using null-coalescing operator
return list ?? new List<Type>();

However, as C# 6 introduced something new, just to overcome the null exception in each line.

var item = list?[index]; // item would be null, if list is null

I am still not very much pushed by this feature, because setting something to null based on null-ness of another is not a good idea. Is it? We can easily do this:

// Get the list
var list = Model.GetList();
var item = new Item(); // Just assume it

if(list != null && index < list.Count) {
    item = list[index];
} else {
    // Show the error
}

// In C# 6, we do
var list = Model.GetList();
var item = list?[index];

if(item == null) {
   // Show the error
}

Now basically, following this approach, we have “easily” removed on conditional block. But still, our code is exposed to:

  1. Null reference exception: We still need to check the item for this, or continue to use the same steps to avoid null reference exception along the track, and finally, tell the client, “Oooooops! There was a problem!”.
  2. Index out of range: In the previous code, we would check the index too, however to do that, we would require a new block in C# 6 code, and that would ultimately lead us to a state where null-conditional operator would lose its meaning.

So, have a look at this code:

List<int> i = null;
var item = i?.First();
 
// Item would be null if list is null. But we managed to minimize errors "yay!"

Technically, we know item is null. But just to check it, we generate the MSIL too,

IL_0000: nop 
IL_0001: ldnull 
IL_0002: stloc.0 // i
IL_0003: ldloc.0 // i
IL_0004: brtrue.s IL_0011
IL_0006: ldloca.s 02 
IL_0008: initobj System.Nullable<System.Int32>
IL_000E: ldloc.2 
IL_000F: br.s IL_001C
IL_0011: ldloc.0 // i
IL_0012: call System.Linq.Enumerable.First
IL_0017: newobj System.Nullable<System.Int32>..ctor
IL_001C: stloc.1 // item
IL_001D: ret

Look at IL_0017, what this does is that it just creates a new instance of an integer of nullable type. So basically, what we have above is:

int? nullable = null; // Int is struct! That is why we append "?" to the type.

The MSIL for this case is:

IL_0000: nop 
IL_0001: ldloca.s 00 // nullable
IL_0003: initobj System.Nullable<System.Int32>
IL_0009: ret

Gotcha! This is the same as to what we had previously. Only difference is the syntax now. Nothing new. So we can also conclude that our item is of type “Nullable<int>“. We already had that, didn’t we? :-)

Remarks and References

This is (yet!) another sugar coat to C# compiler and the syntax. But I like it. However, there are still many things that you may want to learn before digging any deeper into the code.

  1. Nullable Types in C#

Static, Just Got Better!

Another new feature provided by C# 6 is the static keyword in the using statement. So basically, what we have got is a method to include the classes themselves, and not just the namespace packages. What this feature does is already very much popular, so let me just continue to talk about what I had to talk about. I have a few things that I want to talk about… Starting with the first one:

First of all, when you write something like:

using static System.Console;

It doesn’t mean that each time you call “WriteLine” (or other Console’s member functions), it would be the function of Console that gets called. This is more like ambiguity here. For example, the following code:

using System;
using static System.Console;

namespace CSTest
{
    class Program
    {
        static void Main(string[] args)
        { 
            WriteLine("Love for all, hatred for none.");
            Read();
        }

        public static void WriteLine(string message) 
        {
            Console.WriteLine($"'{message}' printed by member function."); 
        }
    }
}

The output of this program is:

Screenshot (6014)

Figure 3: Output of the member function.

So basically, what we can conclude is that even though our Console class is introduced, but compiler prioritizes the member functions. The priority is done in the case of both being static. If your member function is instance, then compiler would raise an error, so in that case you will always have to write the full name for the function with the type it is associated to. Like this:

using System;
using static System.Console;7

namespace CSTest
{
    class Program
    {
        static void Main(string[] args)
        {
             Console.WriteLine("Love for all, hatred for none.");
             Read();
        }

        public void WriteLine(string message) 
        {
            Console.WriteLine($"'{message}' printed by member function."); 
        }
    }
}

This would now run the function from Console class object.

Screenshot (6016)

Figure 4: Message printed by Console.WriteLine function.

This just executes the function. There is another thing that I want to discuss, the static include is not just for the built-in classes, you can use them in your cases and your class objects too. Just for the sake of example, I created a sample class that would allow us to use the functions in a similar behavior.

class Sample
{
    public static void StaticFunction() { }
    public void InstanceFunction() { }
}

// Adding a struct would also work. Static functions would be pulled.
struct SSample
{
    public static void StructStaticFunction() { } 
}

Now, I don’t want to demonstrate the use of struct or class. So, now you can call those functions in your own class as if you were calling them from within the class itself.

Remarks and References

This works for structs and as well as classes. The thing is that this works with the functions who are:

  1. Public, private functions are not shown (of course!)
  2. Static, instance functions require an instance, so compiler doesn’t bother showing them at all.

Static classes are all static, their members are also meant to be static so every member function of a static class would be accessible. However, in case of instance classes, only static functions are visible in the list of IntelliSense.

For more information, please read:

  1. Static Classes and Static Class Members

Auto-properties, and their Improvements

Auto-properties have been available in C# for a long time by now, the thing is that they now introduce a new feature. “Initializers” can be used to initialize these properties to their defaults. A few of the ways that they can be used is like this:

private string Name { get; set; } = "Afzaal Ahmad Zeeshan";

// Without a setter, a readonly
private string Name { get; } = "Afzaal Ahmad Zeeshan";

// Simply read-only
private readonly string Name = "Afzaal Ahmad Zeeshan";

But remember, the last one is a field, not a property. For example, see this:

private string Property { get; set; } = "Afzaal Ahmad Zeeshan";

private string getterOnlyProperty { get; } = "Afzaal Ahmad Zeeshan";

private readonly string readonlyField = "Afzaal Ahmad Zeeshan";

This would be translated as:

IL_0000: nop 
IL_0001: ret

get_Property:
IL_0000: ldarg.0 
IL_0001: ldfld UserQuery.<Property>k__BackingField
IL_0006: ret

set_Property:
IL_0000: ldarg.0 
IL_0001: ldarg.1 
IL_0002: stfld UserQuery.<Property>k__BackingField
IL_0007: ret

// getterOnlyProperty is a getter-only, so not set_ field is available.
get_getterOnlyProperty:
IL_0000: ldarg.0 
IL_0001: ldfld UserQuery.<getterOnlyProperty>k__BackingField
IL_0006: ret

The code is categorized under getters and setters for the properties, and the readonly field is just a variable in the program.

Remarks and References

The new thing introduced in C# 6 is that you can now set the values to a default, in previous versions, you would have to enter a new value in the constructor. However, now you can enter that value right there!

For more please read:

  1. Accessors in C# on MSDN.

Lambdas Have Evolved

If you have ever read my previous posts, you may have known that I am a huge fan of lambda expressions of C#, especially when it comes to handling the events. I have always enjoyed programming, based on these lambdas.

  1. Handling events in WPF in an easy and short hand way

What now can be done is that lambda expressions can be used now to create the functions too (just like before), not just that. They can be used to simplify the getter-only properties too.

The syntax cannot show you how simple it gets. For example, the following code block:

public int Multiply (int a, int b) { return a * b; }

It looks very much simple, and is optimized. However, the MSIL code looks “horrible”.

Multiply:
IL_0000: nop 
IL_0001: ldarg.1 
IL_0002: ldarg.2 
IL_0003: mul 
IL_0004: stloc.0 
IL_0005: br.s IL_0007
IL_0007: ldloc.0 
IL_0008: ret

This is a very long MSIL, which would be translated and would require a lot of CPU cycles. While on the other hand, if you can see this code:

public int Multiply (int a, int b) => a * b;

This lambda expression does the same as what the previous one did. But it is very much performance-efficient. The MSIL generated with this code is like this:

Multiply:
IL_0000: ldarg.1 
IL_0001: ldarg.2 
IL_0002: mul 
IL_0003: ret

How small MSIL code, right? So this demonstrates that using a lambda expression to write our function, we can not just simplify the code size, we can also fine-tune the code that is executed for that.

Another major use of these lambdas is while creating getter-only fields. Now, their topic is already discussed above. But, however, I would like to talk about them again…

public double PI { get; } = 3.14;

The MSIL generated for this would be (you guessed it right!) a get property, and a backing up field. Like this:

get_PI:
IL_0000: ldarg.0 
IL_0001: ldfld UserQuery.<PI>k__BackingField
IL_0006: ret

So basically, what we have is a block, that returns the value of the field that is backing up this property; whenever we call the property. Now, to fine tune it using C# 6, what we can do is something like this:

public double PI => 3.14;

The MSIL for this is really very amazing, just the way we improved the function, we can improve this field too, increasing the access time. So now the MSIL is:

get_PI:
IL_0000: ldc.r8 1F 85 EB 51 B8 1E 09 40 
IL_0009: ret

Pretty much amazing, and short! It just pushes this content, as float, on the stack. Then calls the return to provide the value. The thing is that we don’t need a backing field at all. To make it more readable, please read this:

public string PI => "Afzaal Ahmad Zeeshan";

Now, in the case of a field-backed-property, the value would be stored in the backing field and when we would call this, the backing field would be called (a new call!) and then that value would be returned. However, in this case, what happens is something like this:

get_PI:
IL_0000: ldstr "Afzaal Ahmad Zeeshan"
IL_0005: ret

Simply, this would load the string on the stack, and would then return this value. It is more like having that constant field in your project that gets places wherever it is called from and so on.

Remarks and References

Lambda expressions are definitely very useful, not in just decreasing the syntax size, but also in making the source code size small, too. The lambdas are inherited from “Functional programming”, and well, in the world of object-oriented programming, they have seriously marked their place.

For more, please read:

  1. Functional programming
  2. Lambda expressions in C#
  3. => operator

Points of Interest

In this post, I have talked about the most “popular” and interesting features that C# 6 has introduced to its users. However, until now, most of the times I have only seen the “same song and dance“. Programmers have just talked about the fuss, no one talked about their internal implementation.

In this post, I have shown what difference they have, how they differ, who are just syntactic sugars and which really do have an influence on your programs. Basically, from this list, my favorites are:

  1. String interpolation
  2. Lambda expressions

Others are useful, yet I don’t feel like using them in my own projects. If you have anything else on your mind, that I yet don’t know… please share it with me. :-) I would love to hear.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here