Tag Archives: C# 7.1

Short Walks – C# Pattern Matching to Match Ranges

Back in 2010, working at the time in a variety of languages, including VB, I asked this question on StackOverflow. In VB, you could put a range inside a switch statement, and I wanted to know how you could do that in C#. The (correct) answer at the time was that you can’t.

Fast forward just eight short years, and suddenly, it’s possible. The new feature of pattern matching in C# 7.0 has made this possible.

You can now write something like this (this is C# 7.1 because of Async Main):

static async Task Main(string[] args)
{            
    for (int i = 0; i <= 20; i++)
    {
        switch (i)
        {
            case var test when test <= 2:
                Console.WriteLine("Less than 2");
                break;
 
            case var test when test > 2 && test < 10:
                Console.WriteLine("Between 2 and 10");
                break;
 
            case var test when test >= 10:
                Console.WriteLine("10 or more");
                break;
        }
 
        await Task.Delay(500);
    }
 
    Console.ReadLine();
}

References

https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching

https://visualstudiomagazine.com/articles/2017/02/01/pattern-matching.aspx

Upgrade to C# 7.1

Async Main (C# 7.1)

Another new feature in C# 7.1 is the ability to make a console app deal with Async. Have you ever written a test console app to call an async function; for example, what will this do?

static void Main(string[] args)
{
    MyAsyncFunc();
 
    Console.WriteLine("done");
    
}
 
static async Task MyAsyncFunc()
{
    await Task.Delay(1000);
}

I’m pretty sure that I’ve been asked a question similar to this during an interview, and probably asked the question myself when interviewing others. The way around it in a console app previously was:

static void Main(string[] args)
{
    MyAsyncFunc().GetAwaiter().GetResult();
 
    Console.WriteLine("done");
    
}

However, in C# 7.1, you can do this:

static async Task Main(string[] args)
{
    await MyAsyncFunc();
 
    Console.WriteLine("done");
    
}

Upgrading the Project

Unlike other new features of 7.1, this feature doesn’t afford you the ability to “Control Dot” it. If you try to do this in C# 6, for example, it just won’t compile:

To upgrade, go to the Advanced tab in the Build menu (of project properties):

References

https://github.com/dotnet/roslyn/issues/1695

Default Literals in C# 7.1

One of the new features added to the latest* version of C# is that of a “default” literal. What this means is that you can now use the default keyword as though it were a variable. For example, if you were to want to create a new integer and assign it to its default value; you would write something like this:

int i = default(int);

But, surely C# knows you want a default int? In fact, it does, because if you type:

int i = default(long);

Then it won’t compile. Think of how much you could accomplish if you didn’t have to type those extra five characters! That’s where the default literal comes in:

Default Literal

Default Literal

You can also use the literal in comparison statements:

static void Main(string[] args)
{
    int i = default;
 
    Console.WriteLine(i);
 
    for (i = 0; i <= 3; i++)
    {
        if (i == default)
        {
            Console.WriteLine("i is default");
        }
        else
        {
            Console.WriteLine("i NOT default");
        }
    }
}
Output

Output

IL

What’s happening behind the scenes? The following code:

static void Main(string[] args)
{
    int i = default(int);
 
    Console.WriteLine(i);
    Console.ReadLine();
}

Produces the IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       17 (0x11)
  .maxstack  1
  .locals init ([0] int32 i)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0009:  nop
  IL_000a:  call       string [mscorlib]System.Console::ReadLine()
  IL_000f:  pop
  IL_0010:  ret
} // end of method Program::Main

And the code using the new default literal:

static void Main(string[] args)
{
    int i = default;

    Console.WriteLine(i);
    Console.ReadLine();
}

The IL looks vary familiar:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       17 (0x11)
  .maxstack  1
  .locals init ([0] int32 i)
  IL_0000:  nop
  IL_0001:  ldc.i4.0
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0009:  nop
  IL_000a:  call       string [mscorlib]System.Console::ReadLine()
  IL_000f:  pop
  IL_0010:  ret
} // end of method Program::Main

Footnotes

* C# 7.1 – Latest at the time of writing

References

https://github.com/dotnet/csharplang/blob/master/proposals/target-typed-default.md