Exploring C# 7

Intro

Since we have all been actively celebrating the 20<sup>th</sup> anniversary of Visual Studio, it felt appropriate to post about C# 7!In this post we will explore the features that make C# 7 so promising. I've put together a demonstration C# 7 project, that is available here.

This post contains examples and details on five of the nine new C# 7 features.

  • Pattern matching
  • <inlinecode>out<inlinecode> variables
  • Tuples
  • Local functions
  • <inlinecode>throw<inlinecode> expressions

These are the remaining features, that I do not cover in this post.

  • <inlinecode>ref<inlinecode> locals and returns
  • More expression-bodied members
  • Generalized <inlinecode>async<inlinecode> return types
  • Numeric literal syntax improvements

Pattern Matching

With C# 7 we welcomed the concept of "patterns". This concept allows for the extraction of information when a variable is tested for a certain "shape" and matches a specified pattern. We're able to leverage the "shape" from which we matched on as a declared variable in scope, consuming it as we deem necessary. This is referred to as "dynamic" (or "method") dispatch.

In computer science, dynamic dispatch is the process of selecting which implementation of a polymorphic operation (method or function) to call at run time.It is commonly employed in, and considered a prime characteristic of, object-oriented programming (OOP) languages and systems.

Dynamic dispatch is nothing new to C#, and has been around forever. C# 7 exposes this functionality via constant and type patterns.

Constant Patterns

Constant pattern <inlinecode>null<inlinecode>, similar to <inlinecode>(obj == null)<inlinecode>.

The "is expression" has been expanded

<codeblocktest></codeblocktest>

public void IsExpression(object obj)
{
   if (obj is null) // Constant pattern "obj is null"
   {
       return;
   }
}

<codeblocktest></codeblocktest>

Type Patterns

Look closely at this syntax. This is where we start mixing metaphors. Prior to C# 7 we could use the "is" expression to do simple type assertions <inlinecode>obj is [type]<inlinecode>. Additionally, we all know how to declare a variable <inlinecode>int i<inlinecode>. This new syntax merges these concepts together and is more compound andexpressive.

<codeblocktest></codeblocktest>

public void IsExpression(object obj)
{
   // (obj is int i)
   // "obj is int"     // type assertion "typically evaluates type compatibility at run time"
   //        "int i"   // declaration

   if (obj is int i) // Type pattern "obj is int i"
   {
       // We can then use the "i" (integer) variable
   }

   // Note, the variable "i" is also available in this scope.
   // This is in fact by design, more on that out the "out variable" section
}

<codeblocktest></codeblocktest>


The "switch statement" has been generalized.

The <inlinecode>when<inlinecode> keyword has also been extended, now it not only applies to the <inlinecode>catch<inlinecode> statement but also the <inlinecode>case<inlinecode> labels within a <inlinecode>switch<inlinecode> statement. Consider the following classes:

<codeblocktest></codeblocktest>

class Shape
{
   protected internal double Height { get; }

   protected internal double Length { get; }

   protected Shape(double height, double length)
   {
       Height = height;
       Length = length;
   }
}

class Circle : Shape
{
   internal double Radius => Height / 2;

   internal double Diameter => Radius * 2;

   internal double Circumference => 2 * Math.PI * Radius;

   internal Circle(double height, double length)
       : base(height, length) { }
}

class Rectangle : Shape
{
   internal bool IsSquare => Height == Length;

   internal Rectangle(double height, double length)
       : base(height, length) { }
}

<codeblocktest></codeblocktest>


Now imagine that we have a collection of these <inlinecode>Shape<inlinecode> objects, and we want to print out their various details - we could use "pattern matching" as such:

<codeblocktest></codeblocktest>

static void OutputShapes(IEnumerable<shape> shapes)</shape>
{
   foreach (var shape in shapes)
   {
       // Previously, this was not permitted. Case labels had to be concrete
       // such as enums, numerics, bools, strings, etc.
       switch (shape)
       {
           case Circle c:
               WriteLine($"circle with circumference {c.Circumference}");
               break;
           case Rectangle s when (s.IsSquare):
               WriteLine($"{s.Length} x {s.Height} square");
               break;
           case Rectangle r:
               WriteLine($"{r.Length} x {r.Height} rectangle");
               break;
           default:
               WriteLine("This is not a shape that we're familiar with...");
               break;
           case null:
               throw new ArgumentNullException(nameof(shape));
       }
   }
}

<codeblocktest></codeblocktest>


As you can see, we are able to more easily reason about the specific <inlinecode>shape<inlinecode> in context. For example, with each iteration of our collection we <inlinecode>switch<inlinecode> on the <inlinecode>shape<inlinecode>. If the <inlinecode>shape<inlinecode> is an instance of the <inlinecode>Circle<inlinecode> subclass, we'll execute the <inlinecode>case<inlinecode> label "Circle" and we get the instance declared asits type in the variable <inlinecode>c.<inlinecode> Likewise, if the <inlinecode>shape<inlinecode> is a <inlinecode>Rectangle<inlinecode> and that rectangle <inlinecode>s<inlinecode> just so happens to also be a square <inlinecode>when (s.IsSquare)<inlinecode> evaluates to <inlinecode>true<inlinecode> - we will then execute the square <inlinecode>case<inlinecode> label. If the <inlinecode>shape<inlinecode> is an instance of a <inlinecode>Rectangle<inlinecode> but not a square, we execute the"Rectangle" <inlinecode>case<inlinecode> label. Notice we still have <inlinecode>default<inlinecode> fall-thru. Finally, we can also have a "null" <inlinecode>case<inlinecode> label.

out variables

<inlinecode>.NET<inlinecode> developers are more than familiar with the <inlinecode>Try*<inlinecode> pattern, but as a refresher this is what it looks like. Imagine we are trying to parsea <inlinecode>System.String<inlinecode> input value as a <inlinecode>System.Int32<inlinecode>. Imagine that the consumer doesn't really care if it is parsed, they're fine with a <inlinecode>default(int)<inlinecode> if it fails.

<codeblocktest>

public int ToInt32(string input)
{
   int result;
   if (int.TryParse(input, out result))
   {
       return result;
   }

   return default(int);
}

<codeblocktest>

Let's quickly recap this. First, we declare a variable namely <inlinecode>result<inlinecode>. We then invoke the <inlinecode>int.TryParse<inlinecode> which returns a <inlinecode>bool<inlinecode> whether or not theparse was successful. If <inlinecode>true<inlinecode> then the declare <inlinecode>result<inlinecode> variable is not equal to the parsed int value. If the input was <inlinecode>"12"<inlinecode>, then <inlinecode>result<inlinecode> would be <inlinecode>12<inlinecode>. If the <inlinecode>input<inlinecode> was <inlinecode>"Pickles"<inlinecode>, then the return from the invocation to the <inlinecode>ToInt32<inlinecode> would be <inlinecode>0<inlinecode> as <inlinecode>int.TryParse<inlinecode> would return <inlinecode>false<inlinecode>.


Now with C# 7 we can declare our out variable inline as follows:

<codeblocktest></codeblocktest>

public int ToInt32(string input)
{
   // Note: the declaration is inline with the out keyword
   if (int.TryParse(input, out int result))
   {
       return result;
   }

   return default(int);
}

<codeblocktest></codeblocktest>


The scope of the result variable is identical to the previous example, as it actually "leaks" out to the if statement. We can re-write this evenmore expressively:

<codeblocktest> </codeblocktest>

public int ToInt32(string input) => int.TryParse(input, out var result) ? result : result;

<codeblocktest></codeblocktest>


A few things you might notice. First, this is now a single line as we can express this with the lambda operator. We leverage the ternary operator aswell. Additionally, we can use the <inlinecode>var<inlinecode> keyword for our declaration. And since the <inlinecode>result<inlinecode> variable is in scope we can use it as both return cases.If unable to be parsed, it is in fact a <inlinecode>default(int)<inlinecode> anyways.

Tuples

Most developers are familiar with <inlinecode>System.Tuple<t[,t1...]><inlinecode>. This class has served us well all the while it has been around. One of the advantages is that it exposes <inlinecode>readonly<inlinecode> fields - from the values that it is instantiated with. This was also great for equality comparisons and even using the tuple as a dictionary key.

In C# 7 we have a new syntax for expressing tuples. Enter the <inlinecode>ValueTuple<inlinecode>, and as the name implies - this is a <inlinecode>struct<inlinecode> instead of a <inlinecode>class<inlinecode>. Thereare obvious performance gains from using a lightweight value-type over the allocation of a <inlinecode>class<inlinecode>.

<codeblocktest></codeblocktest>

void LegacyTuple()
{
   var letters = new Tuple<char, char="">('a', 'b');</char,>

   // Values were accessible via these Item* fields.
   var a = letters.Item1;
   var b = letters.Item2;
}

<codeblocktest></codeblocktest>


This wasn't overly exciting from an API perspective, as the field names do not really imply anything about their intention.

<codeblocktest></codeblocktest>

void ValueTuple()
{
   var letters = ('a', 'b');
   var a = letters.Item1;
   var b = letters.Item2;

   // Note: ToTuple extension method
   var systemTuple = letters.ToTuple();
   var c = systemTuple.Item1;
   var d = systemTuple.Item2;
}

<codeblocktest></codeblocktest>


You might notice that the syntactic sugar is pouring over this new feature. This is referred to as a "tuple literal". We dropped the entire <inlinecode>new<inlinecode> keyword usage, as well as specifying the types. They are all inferred and in fact known, IntelliSense proves this immediately. But we still have the issue of these tuples not being very API friendly. Let's explore how we can give them custom names.

<codeblocktest></codeblocktest>

void MoreValueTuples()
{
   var lessonDetails =
       (Subject: "C# Language Semantics", Category: Categories.Programming, Level: 300);

   // Note: IntelliSense now hides Item1, Item2 and Item3
   // Instead we are provided with the following:

   var subject = lessonDetails.Subject;    // string
   var category = lessonDetails.Category;  // Categories [enum]
   var level = lessonDetails.Level;        // int
}

<codeblocktest></codeblocktest>

Deconstruction

Now that we see how we can instantiate a <inlinecode>ValueTuple<inlinecode>, let's take a look at how we can declare one for usage.</inlinecode></inlinecode>

<codeblocktest></codeblocktest>

void DeconstructionExamples()
{
   var lessonDetails =
       (Subject: "C# Language Semantics", Category: Categories.Programming, Level: 300);

   // We can deconstruct in three various ways
   // First, the fully qualified type
   (string subject, Categories category, int level) = lessonDetails;

   // Next using the var keyword per named declaration
   (var subject, var category, var level) = lessonDetails;

   // Finally, omitting any type declaration and using var wholeheartedly
   var (subject, category, level) = lessonDetails;
}

There are often questions about how deconstruction is implemented, and whether or not it is ordinal based. For the <inlinecode>ValueTuple<inlinecode> it is in factordinal based. However, note that deconstruction is not actually limited to tuples. With </inlinecode></inlinecode>C# 7 any <inlinecode>object<inlinecode> that defines a <inlinecode>public void Deconstruct<inlinecode> method can be deconstructed. Consider the following:

<codeblocktest>

class Person
{
   private readonly (string First, string Middle, string Last) _name;

   private readonly DateTime DateOfBirth _dateOfBirth;

   public Person((string f, string m, string l) name, DateTime dob)
   {
       _name = name;
       _dateOfBirth = dob;
   }

   public void Deconstruct(out double age,
                           out string firstName,
                           out string middleName,
                           out string lastName)
   {
       age = (DateTime.Now - _dateOfBirth).TotalYears;
       firstName = _name.First;
       middleName = _name.Middle;
       lastName = _name.Last;
   }
}

<codeblocktest>


Now that the <inlinecode>Person<inlinecode> is defined with this <inlinecode>Deconstruct<inlinecode> method, we can deconstruct it following the same ordinal based semantics.

<codeblocktest>

void DeconstructNonTuple()
{
   var person = new Person(("David", "Michael", "Pine"), new DateTime(1984, 7, 7));

   (int age, string first, string middle, string last) = person;

   // Note: to partially deconstruct you can ignore a specific ordinal by using the _
   // This does not actually naming the ordinal variable, but truly ignoring it.

   var (_, _, _, _) = person;          // Ignore all, not very useful
   var (_, firstName, _, _) = person;  // Cherry-pick first name
}

<codeblocktest>


Comparing Anonymous object vs. ValueTuple

At first glance tuples look almost like anonymous objects. They are in fact very different. An anonymous object is actually a reference type, whereas a <inlinecode>ValueTuple<inlinecode> is a <inlinecode>struct<inlinecode> - value type. Also, you can only return an anonymous <inlinecode>object<inlinecode> from a method as an object which isn't very API friendly. Within a fluent <inlinecode>LINQ<inlinecode> chained method anonymous objects are great and will still be normal for projection.

Local Functions

At first glance, local functions seem a bit odd. I've heard people say, "this method is starting to look like a class". At first, I was one of these people too. Once you get used to the idea and see the benefits it really does make sense. Here is a quick comparison of the two, note the benefits of local functions as they compare to lambdas.

Lambda(s)Local Function(s)DetailsGenerics: Local functions allow for the use of generics

Iterators: The yield keyword is valid within local functions

Recursion: Local functions support recursion

Allocatey: Delegates require an object allocation

Potential Variable Lifting: Implicitly captured closure is non-existent

It is vital to understand that local functions are not a replacement for <inlinecode>Action<t[,t1...]> or Func<t[,t1...]><inlinecode>. These delegate declarations are still needed as parameters to enable lambda expression arguments. If you see the #notasugly hashtag, this was coined by Mads Torgersen.

More efficient

When using local functions, there is no <inlinecode>object<inlinecode> created - unlike delegates that require an object for it to be used. Likewise, local functionshelp to alleviate another issue with lambdas in that they do not need to implicitly capture a variable longer than it is potentially needed. In C# lambdas capture values by reference, meaning that garbage collection may not be able to correctly clean up code that is "allocatey".

Declaration

With local functions, the declaration of the local function can actually occur after the <inlinecode>return<inlinecode> statement - as long as it is within the method bodyin which it is consumed. If you're familiar with some of the implementations of the <inlinecode>LINQ<inlinecode> extension methods on <inlinecode>IEnumerable<t><inlinecode>, you would know a lotof the methods are defined with argument validation followed by the instantiation of "Iterator" classes, where these classes do the actual work.

Because of deferred execution, iterators do not actually execute validation logic until they are iterated - for example invoking <inlinecode>.ToList()<inlinecode>, <inlinecode>.ToArray()<inlinecode>,or simply using them in a <inlinecode>foreach<inlinecode> statement. Ideally, we would like our iterators to "fail-fast" in the event of being given invalid arguments. Let'simagine that the <inlinecode>.Select<inlinecode> extension method was implemented as follows:

<codeblocktest></codeblocktest>

public static IEnumerable<tresult> Select<t, tresult="">(this IEnumerable<t> source, </t></t,></tresult>
                                                          Func<t, tresult=""> selector)</t,>
{
   if (source == null) throw new ArgumentNullException(nameof(source));
   if (selector == null) throw new ArgumentNullException(nameof(selector));

   foreach (var item in source)
   {
       yield return selector(item);
   }
}

<codeblocktest></codeblocktest>


Since this method is written as an iterator, the validation is skipped until it's iterated. With C# 7 we can use local function to get both "fail-fast" validation and the iterator together.

<codeblocktest></codeblocktest>

public static IEnumerable<tresult> Select<t, tresult="">(this IEnumerable<t> source, </t></t,></tresult>
                                                          Func<t, tresult=""> selector)</t,>
{
   if (source == null) throw new ArgumentNullException(nameof(source));
   if (selector == null) throw new ArgumentNullException(nameof(selector));

   return iterator();

   IEnumerable<tresult> iterator()</tresult>
   {
       foreach (var item in source)
       {
           yield return selector(item);
       }
   }
}

<codeblocktest></codeblocktest>

throw expressions

Leveraging some pre-existing C# functionality - <inlinecode>null<inlinecode> coalescing, we can now <inlinecode>throw<inlinecode> when a value is evaluated as <inlinecode>null<inlinecode>. A common validationmechanism is to throw if an argument is null. Consider the following:

<codeblocktest></codeblocktest>

class LegacyService : IService
{
   private readonly IContextProvider _provider;

   public LegacyService(IContextProvider provider)
   {
       if (provider == null)
       {
           throw new ArgumentNullException(nameof(provider));
       }

       _provider = provider;
   }
}

<codeblocktest></codeblocktest>


With C# 7 we can simplify this with the throw expression.

<codeblocktest></codeblocktest>

class ModernService : IService
{
   private readonly IContextProvider _provider;

   public ModernService(IContextProvider provider)
   {
       _provider = provider ?? throw new ArgumentNullException(nameof(provider));
   }
}

<codeblocktest></codeblocktest>


If the given <inlinecode>provider<inlinecode> argument is <inlinecode>null<inlinecode> we'll coalesce over to the <inlinecode>throw<inlinecode> expression.

From C# 6 to C# 7, then and now!

I have a presentation that I have been fortunate enough to give at some regional conferences. One of these occasions was recorded, and I felt itmade sense to share it here - Enjoy!!

Further Reading

Get your project started today

Get in Touch