What's new in C# 6.0

Some pretty interesting features appeared in this version of the language, in many cases, linked to everyday problems and suggestions of developers worldwide. Also, as stated in Table 1 of this chapter, the really huge, meaningful improvement comes from a set of functionalities linked to Roslyn Services, which provide a different bunch of possibilities related to the editing and compiling features of the IDE. We will cover these in Chapter 8, Open Source Programming.

However, Roselyn is not the only interesting option that appeared in C# 6.0. There are a number of minor but very useful and syntactic "sweets" this version includes, which help the coder write more succinct expressions and reduce the occurrence of bugs. Let's start with something called string interpolation.

String interpolation

String interpolation is a way to simplify string expressions that contain C/C++ style interpolation. For instance, instead of writing the classic Console.Write("string {0}", data) composition, we can now express this by simply including the identifier inside curly brackets, so the previous expression would become $"string {data}" as far as we precede the string with the $ symbol in order to make it work.

Note that we can mix the @ symbol with $ given that the $ symbol goes before the @ symbol.

Moreover, you can use the {} area to include a C# expression that will be correctly evaluated at runtime and converted into a string by calling the ToString method so that it's not limited to identifiers. Thus, we can even include file I/O operations—like we do in the following code—and get the results.

To test this, I have a text file (TextFile.txt) with a line of content, which is presented in the output accompanied by a string literal in a single line of code:

Console.WriteLine($"File contents: {File.ReadAllText("TextFile.txt")}");
Console.ReadLine();

As you can see in the next capture, expressions inside the curly braces are totally evaluated and the result inserted in the output string:

String interpolation

This technique, besides simplifying expressions, can be used easily in conjunction with other new C# 6.0 features, such as Exception filters.

Exception filters

Another addition refers to exceptions. Exception filters provide a way to personalize any occurred exception depending on a condition that can be coded using any valid C# expression, which should be located next to the new when sub-clause that might follow any Catch clause now.

In the previous code, let's suppose that we want to create a conditional test for an exception that doesn't have much to do with the exception itself (or maybe it does, but that's not the case here). Or, we can even suppose that we want to catch a situation related to an external state, such as the system's date/time or what have you.

The following code catches a situation in which the previous file exists but produces an exception on Saturdays, to say something bizarre. We can modify the code in this way:

string filename = "TextFile.txt";
try
{
  Console.WriteLine($"File contents: {File.ReadAllText(filename)}");
  Console.ReadLine();
}
catch when (File.Exists(filename) && 
  DateTime.Today.DayOfWeek == DayOfWeek.Saturday)
{
  Console.WriteLine("File content unreadable on Saturdays");
}
catch (Exception ex)
{
  Console.WriteLine($"I/O Exception generated:{ex.Message}");
}

This possibility provides us with new ways to catch exceptions linked to conditions that don't belong (necessarily) to the exception context but to any other situation; just consider that the expression can be much more complex than that in the demo code.

The nameof operator

The nameof operator is a contextual keyword with a syntax similar to typeof, which yields the name of any program element (usually, an identifier). Or, if you want, it converts the filename variable of the previous example into filename.

This approach offers several advantages. First, if we need the name of such an element, no reflection technique is required. Besides, the compiler is going to guarantee that whatever we pass as an argument to the operator is a valid element; also, it integrates with the editor's Intellisense and behaves better in some refactoring scenarios.

It's useful in try-catch structures as well, for example, when indicating the reason for a failure specifying the name of the element that causes the exception and even in attributes, as the "official" example of MSDN suggests (refer to https://msdn.microsoft.com/en-us/library/dn986596.aspx):

[DebuggerDisplay("={" + nameof(GetString) + "()}")]
class C
{
  string GetString() { return "Hello"; }
}

The null-conditional operator

This operator is the latest member of the family of features designed to deal with null values in C#. Since version 1.0, we could, of course, check (value == null) within a conditional statement to avoid undesired failures.

Later, Nullable types arrived (remember, appending a ? symbol to a variable declaration allows it to be null, and these types include a Boolean HasValue property to check this situation):

int? x = 8;
x.HasValue // == true if x != null

When a conversion is required, the TryParse method of many basic types allows us to check for valid values (not only null).

As language evolved, new ways of dealing with null values kept coming up. In C# 4.0, one of the most useful things was the null-coalescing operator. It works a bit like the ? operator: it locates itself between two elements to check for nullability, and if the left-hand side is not null, it returns it; otherwise, it returns the right-hand side operand. It's so simple that it even lets you mix it with string interpolation in this way:

string str = null;
Console.WriteLine(str ?? "Unspecified");
Console.WriteLine($"str value: { str  ?? "Unspecified"}");

We get the expected result in the console:

The null-conditional operator

So, the previous code writes Unspecified in the console, since str is null.

Now in C# 6.0, we have another ability: the null conditional operator, or null propagation operator (or even, the Elvis operator, as it's called by some members of the C# team, assuming that the two lower points are a couple of eyes and the higher part of the question mark the toupee!), which can be inserted in an expression, and it stops evaluating the right-hand side of the expression if the value of the adorned element with the operator is not present. Let's understand this better through an expression:

If we want to print out the length of the str string in the previous case, we can simply add another Console sentence, such as Console.WriteLine(str.Length.ToString());. The problem is that it would provoke an exception when trying to access the Length property of str.

To fix this, we can use this operator in very simple way:

Console.WriteLine(str?.Length.ToString());

By including the null conditional ? operator, the Length property is not even accessed, so there's no exception, and we will get the expected output (an empty string, in this case).

Let's put everything together in a block of code so that we compare different behaviors for null and non-null strings:

// Case 2
string str = null;
string str2 = "string";
Console.WriteLine(str ?? "Unspecified");
Console.WriteLine(str2 ?? "Unspecified");
Console.WriteLine($"str value: { str ?? "Unspecified"}");
Console.WriteLine($"str2 value: { str2 ?? "Unspecified"}");
Console.WriteLine($"str Length: {str?.Length}");
Console.WriteLine($"str2 Length: {str2?.Length}");
Console.ReadLine();

This code compiles with no problems and generates the following output:

The null-conditional operator

Observe the fifth entry: no value is presented because no evaluation has been made of the Length of str. There are numerous cases in which this is just the operator we need: it could be checking a null delegate before invocation or inserting it right before any common ToString call for usual conversions.

Auto-property initializers

Auto-property initializers are another improvement that helps manage immutable properties (those that once given a value, don't change along the life of the application).

In preceding versions, declaring read-only properties was kind of redundant. You had to declare the read-only backup private field and take care of its initialization and, later, provide an explicit implementation of such property (instead of using the common auto-properties). Finally, to access the value, a property-get member was included. This was the way good practices recommended you to create this particular type of data.

This is also why auto-properties are so handy. For example, if our application captures the current username and operating system version of the machine, it's installed in a pair of read-only properties. It suffices to indicate this in the following manner:

public static string User { get; } = Environment.UserName;
public static string OpSystem { get; } = Environment.OSVersion.VersionString;
static void Main()
{
  Console.WriteLine($"Current {nameof(User)}: {User}");
  Console.WriteLine($"Version of Windows: : {OpSystem}");
}

So, we're using a more concise syntax to express the same idea and obtain the same results as what we achieved with the classical approach:

Auto-property initializers

Static using declarations

Another way to simplify syntax is based on the idea of extending directives in the code to make them capable of referencing static members of the .NET Framework and using them in the same way as we use other declarations mentioned in a using directive.

That is, we can include a declaration like this:

using static System.Math;

From this point, any reference to a member of the Math class can be done directly without an indication of the namespace (and the static class) it belongs to:

// Static reference of types
Console.WriteLine($"The square root of 9 is {Sqrt(9)}");

Note that we're using string interpolation all over the demos, since the simplification it allows is very useful, especially for these console-type snippets (I omitted the output in this case, you can figure it out...).

Moreover, there's another typical case in which this functionality is important: when we use Enum members. Most of the time, we already know the possible values, so if we are indicating a typical Enum, such as the day of the week, we can indicate the corresponding Enum type as a static:

using static System.DayOfWeek;

Then, use it just like earlier (remember, the number of Enum types in .NET is quite large):

Console.WriteLine($"Today is {Friday}");

We even keep things more generic, using the nameof operator we already saw:

DayOfWeek today = DateTime.Today.DayOfWeek;
Console.WriteLine($"{nameof(today)} is {today}");

So, we would still get the expected output, though expressed in a much more generic way:

Static using declarations

Since the demo is a Console application, even Console can be referenced in this way; so, consider that we want to change the colors of the output in the Console instead of writing something like this:

ConsoleColor backcolor = ConsoleColor.Blue;
ConsoleColor forecolor = ConsoleColor.White;
Console.BackgroundColor = backcolor;
Console.ForegroundColor = forecolor;

We can put it all together in much simpler way (of course, some developers may argue that this is a matter of syntactic tastes.). At the top of the code, we declare the following:

using static System.Console;
using static System.ConsoleColor;

Then, the rest is all simplified:

BackgroundColor = DarkBlue;
ForegroundColor = White;
WriteLine($"{nameof(today)} is {today}");
WriteLine($"Using {nameof(BackgroundColor)} : {BackgroundColor}");
WriteLine($"Using {nameof(ForegroundColor)} : {ForegroundColor}");
Read();

The expected output is presented in a tuned Console this time:

Static using declarations

Expression bodied methods

When coding lambda expressions, we've seen that we could omit the curly braces used to indicate the body of a method in order to simplify the code. Now, we can do something similar in methods, allowing us to express overriding in a simpler way. Consider this example code:

using static System.Console;
namespace Chapter03_03
{
  public class ExpressionBodied
  {
    public static void Main()
    {
      ExpressionBodied eb = new ExpressionBodied();
      WriteLine(eb.ToString());
    }
    public string Name { get; } = "Chris";
    public string LastName { get; } = "Talline";
    public override string ToString() => $"FullName: {LastName}, {Name}";
  }
}

Overriding the ToString() method is expressed using a simpler manner that contains string interpolation. It's pretty readable and concise, and it works just the same as it did in previous versions. (I also omitted the output, but you can easily infer it).

The same idea is valid to declare calculated properties in a class, for example. If we need to complete the previous class with a calculated property that returns a Boolean indicating whether the FullName member is longer that 12 characters (we call it FullNameFits), we can write this:

public bool FullNameFits => ((Name.Length + LastName.Length) > 12) ? false : true;

As you can see, this becomes much more concise and expressive than it was before this version.

Index initializers

Let's, finally, mention another feature related to initializers. Until now, when initializing index setters, we had to do it in separate statements. To put this in context, now if we have to initialize an array of values that coincides with certain numbers that are already well known, as is the case with Web Errors Dictionary (that is, 404-Not Found, and so on), we can define it in this way (all in one sentence):

Dictionary<int, string> HttpWebErrors = new Dictionary<int, string>
{
  [301] = "The page requested has been permanently moved",
  [307] = "The requested resource is available only through a proxy",
  [403] = "Access forbidden by the server",
  [404] = "Page not found. Try to change the URL",
  [408] = "Request timeout. Try again."
};

So, right in the initialization process, we define the keys required (or, at least, initially required) regardless of whether they have to be changed later or not.

In all, we can say the C# new features in version 6.0 is not very deep and significant, especially when compared to version 4.0, to mention just one. However, they're quite useful and they reduce the required code on many scenarios in which the programmer already knows the structure to write well enough so as to get rid of some of the verbosity connected to some programming structures.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.34.197