C#7.3

C# 7.3 upgrades can be categorized into these:

  1. Enabling more efficient safe code: This theme provides features that enable safe code to be as performant as unsafe code.
  2. Make existing features better: This theme provides incremental improvements to existing features.
  3. New compiler options: -publicsign and -pathmap

The -publicsign compiler option instructs the compiler to sign the assembly using a public key

The -pathmap compiler option instructs the compiler to replace source paths from the build environment with mapped source paths.

1. Enabling more efficient safe code:

FIXED

The fixed statement prevents the garbage collector from relocating a movable variable. The fixed statement is only permitted in an unsafe context. fixed can also be used to create fixed size buffers.

Starting with C# 7.3, the fixed statement operates on additional types beyond arrays, strings, fixed-size buffers, or unmanaged variables.

Any type that implements a method named GetPinnableReference can be pinned. The GetPinnableReference must return a ref variable to an unmanaged type.

Before

unsafe struct S
{
    public fixed int myFixedField[10];
}
class C
{
    static S s = new S();

    unsafe public void M()
    {
        fixed (int* ptr =          s.myFixedField)
        {
            int p = ptr[5];
        }
    }
}

In earlier versions of C#, you need to declare a second fixed pointer

After

unsafe struct S
{
    public fixed int myFixedField[10];
}
class C
{
    static S s = new S();

    unsafe public void M()
    {
        int p = s.myFixedField[5];
    }
}

Without pinning the variable p inside a separate fixed statement. You don't need to declare a separate int* variable.

ref local variables may be reassigned

ref locals may be reassigned to refer to different instances after being initialized. The following code now compiles

ref VeryLargeStruct refLocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.

stackalloc arrays support initializer

The stackalloc keyword is used in an unsafe code context to allocate a block of memory on the stack.

Beginning with C# 7.3, you can use array initializer syntax for stackalloc arrays. All the following declarations declare an array with three elements whose values are the integers 1, 2, and 3

// Valid starting with C# 7.3
int* first = stackalloc int[3] { 1, 2, 3 };
int* second = stackalloc int[] { 1, 2, 3 };
int* third = stackalloc[] { 1, 2, 3 };

The use of stackalloc automatically enables buffer overrun detection features in the common language runtime (CLR). If a buffer overrun is detected, the process is terminated as quickly as possible to minimize the chance that malicious code is executed.

Enhanced generic constraints

You can now specify the type System.Enum or System.Delegate as base class constraints for a type parameter.

You can also use the new unmanaged constraint, to specify that a type parameter must be an unmanaged type. An unmanaged type is a type that isn't a reference type and doesn't contain any reference type at any level of nesting.

2. making existing feature better:

Tuples support == and !=

These operators work by comparing each member of the left argument to each member of the right argument in order. These comparisons short-circuit. They will stop evaluating members as soon as one pair is not equal.

var left = (a: 5, b: 10);
var right = (a: 5, b: 10);
Console.WriteLine(left == right); // displays 'true'

// lifted conversions
(int? a, int? b) nullableMembers = (5, 10);
Console.WriteLine(left == nullableMembers); // Also true

// converted type of left is (long, long)
(long a, long b) longTuple = (5, 10);
Console.WriteLine(left == longTuple); // Also true

// comparisons performed on (long, long) tuples
(long a, int b) longFirst = (5, 10);
(int a, long b) longSecond = (5, 10);
Console.WriteLine(longFirst == longSecond); // Also true

(int a, string b) pair = (1, "Hello");
(int z, string y) another = (1, "Hello");
Console.WriteLine(pair == another); // true. Member names don't participate.
Console.WriteLine(pair == (z: 1, y: "Hello")); // warning: literal contains      different member names

//nested tuples

(int, (int, int)) nestedTuple = (1, (2, 3));
Console.WriteLine(nestedTuple == (1, (2, 3)) );

"in" method overload resolution tiebreake

When the in argument modifier was added, these two methods would cause an ambiguity:

static void M(S arg);
static void M(in S arg);

To call the version with the readonly reference argument, you must include the in modifier when calling the method.

Extend expression variables in initializers

The syntax added in C# 7.0 to allow out variable declarations has been extended to include field initializers, property initializers, constructor initializers, and query clauses. It enables code such as the following example

public class B
{
   public B(int i, out int j)
   {
      j = i;
   }
}

public class D : B
{
   public D(int i) : base(i, out var j)
   {
      Console.WriteLine($"The value of 'j' is {j}");
   }
}