Wed 25 Apr 2018 — Sun 28 Oct 2018

C# Recent Features

These notes are about newish features in C#, not the basics.

Here's the C# version history. I've written up the parts of it that I care about.

The last version of C# I had used when I started writing this was 3.5.

Once again, C# seems to have spanked Java (which has just about gotten around to a naff version of LINQ) in terms of development velocity.

Duck Typing

The dynamic keyword does run-time duck typing. I'm surprised they put this in.

This is done using the first one we find of:

  • COM IDispatch
  • IDynamicMetaObjectProvider
  • Reflection

We should probably implement IDynamicMetaObjectProvider in most cases to make things faster? But performance benchmark it first.

Not to be confused with var, which is type inference.

Covariant and Contravariant Type Parameters

In generics, we can now use the out and in keywords, like Thing<out X>

Technical Term C# Term Java Term
Covariance out extends
Contravariance in super

If we don't specify these, our type is invariant.

COM

The ref keyword (call by reference) is now optional when calling COM methods.

It will magically create a variable and refer to it for you.

There is also a thing called Embedded Interop. This means you don't have to ship so much code when you use COM.

Caller Info

You can put one of these attributes on a string typed method parameter, and default that parameter's value to null:

  • CallerMemberName
  • CallerFilePath
  • CallerLineNumber

When the method is called, the default will be overridden with some magic reflection info.

A useful debugging and tracing tool.

Async and Await

Built on the Task<T> type.

When you declare a method (or lambda), you can mark it with the async keyword.

When you call an async method, you use the await keyword. You can only use this inside a method which is itself declared async.

Await is what actually passes control over. Behind the scenes, it will cleverly sort out a reactor loop, and yield execution whenever it comes to the await keyword.

This is useful for I/O operations, like waiting for a database, a network, or another process. However, you can use the same pattern together with the Task Parallel Library (Java equivalent is Fork/Join) if you actually just want to do some CPU work in parallel.

Useful static methods on the Task class:

Task.WhenAll(tasks)
returns a Task which is complete when all the tasks are complete.
Task.WhenAny(tasks)
returns a Task which is complete when the first task completes.
Task.Delay()
returns a Task which is complete later.
Task.Run(task)
run the Task on another thread.

Overall, using async and await is still going to turn the control flow of your program inside-out.

As of C# 7.1, you can also declare your Main method to be async and have it return a Task.

TODO Exceptions with Async and Await

Exceptions will end up on the Task object itself.

It appears that the await keyword does some exception handling magic for us though?

Look into this some more — it's usually a massive pain with this sort of thing.

Progress Reports

There is an IProgress<T> interface, which is useful for providing notifications on how far along a task is.

Your asynchronous method should accept this as a parameter.

Cancellation

Asynchronous methods may accept a CancellationToken as a parameter.

Async void

If you are declaring a method which has no return type, you can't use Task<T>. You need to write async void.

This will really mess up your exception handling. Avoid it if you can.

Imports

Added the using static keyword. Not clear if it works with enums — I should check.

Exceptions

You can now put a guard condition on your catch blocks. These are called exception filters.

try {
    Whatever();
} catch (Exception ex) when (MyCondition()) {
    // do some stuff
}

This is useful for debugging, if you have break on uncaught exceptions turned on.

Additionally, you can now throw exceptions as part of an expression (for example, inside a ternary).

Methods

Defaults

You can specify a default value when declaring your method parameters. When calling a method, you can call those parameters by name in any order (after the positional arguments).

It looks just like every other language which has this feature.

Lambda Syntax

Method declarations can now also use the lambda => syntax for brevity. Since C# 7.0, you can do use this syntax for constructors and finalizers too.

Scope Functions

You can now define functions inside a method, to restrict them to the scope. They are compiled to private methods, so they still has access to this.

Pass By Reference

If a method has out parameters, you can now declare them inside the method callsite. They then become available in the scope of your calling method. This saves some lines of code, but could look confusing sometimes. You can also pass _ as a discard value for out parameters.

DoSomething(out string result);
Console.WriteLine(result);

There are some extra keywords relating to passing by reference and returning references.

in
this method parameter is passed by reference, but not modified.
ref
this method's return value is a reference.
ref readonly
this method's return value is a reference which you can't modify.

When you return a reference, you have to write return ref myReturnValue. When you call a method which returns a reference, you must store the result in a ref variable.

ref var thing = ref SomeRefReturningMethod();

The main point of the above is for improving the performance and readability of mathematical code where you don't want to have lots of allocations.

To help with this, we also have some extra keywords when declaring structs:

readonly struct
this struct can't be changed, when used as a method parameter should be an in parameter.
ref struct
this struct will always be stack allocated.

Properties

When you declare a property, you can now set its initial value. This works even if it's read-only.

The value must be statically computable.

public String Thing {get;} = "Oy oy " + "savaloy!";

Like methods, property getters can now use the lambda => syntax.

Null Propagation

There's a new ?. operator, which is like the normal . operator, but with a null check first.

// This long winded way of writing things
var thing = x == null ? x : x.whatever;

// Is the same as this new syntax
var thing2 = x?.whatever;

For method calls, you need to use the Invoke() method.

var thing = x.?MyMethod.?Invoke();

Strings

We can do quick string formatting with a new kind of interpolated string literal.

$"String greeting = Hello, {someExpressionToInsert, minCharacters, formatString}!";

There's also now a nameof function. This is actually a compile time thing rather than a real function call, and turns a variable, type or function into a string.

Dictionary Initializer

Wrap each key, value pair in some braces.

I could have sworn this existed before?

var thing = new Dictionary<string, int> {
    {"x", 0},
    {"y", 1}
};

Tuples

C# now has tuples, which are mostly written like in every other language.

You can give the fields of the tuple names by using an association syntax var thing = (x: x, y: y).

As of C# 7.1, these field names are inferred anyway if you write var thing = (x, y).

It also has simple destructuring. To make this work, you type need to implement the Deconstruct method. The parameters you're outputting should be out parameters, obviously.

You can add destructuring to a type you don't control using extension methods.

You can use _ as a discard value when destructuring.

Access Modifiers

There's now the combination keyword private protected (containing class or derived types which are in the same assembly).

This compares to protected (derived types) and protected internal (classes in same assembly or derived types).

This seems a bit pointless to me actually.

Pattern Matching

The switch statement has been extended to understand about type and structure.

switch(thing) {
    case null:
        // matched null
        break;
    case 0:
        // matched some other value
        break;
    case int val:
        // matched on a type
        break;
    case string s when s > "hello":
        // matched on a type and condition
        break;
    default:
        // happens last
        break;
}

The is statement has also been extended. It can match on value and type, but doesn't get the extra condition (which you can just put on with && anyway).