What's New in C# 7.3?

A look at the small but useful new features introduced in C# 7.3, including tuple equality, enum constraints, and performance improvements for working with `in` parameters.

While not a major version release, C# 7.3, which shipped in May 2018, brought a collection of small but welcome improvements to the language. These features continue the theme of making C# more convenient for everyday coding and enabling higher performance in low-level scenarios.

Let's look at some of the most useful additions in C# 7.3.

1. Tuple Equality

C# 7.0 introduced a powerful new syntax for creating and working with tuples. C# 7.3 enhances this by making tuples comparable with the == and != operators.

var tuple1 = (a: 1, b: 2);
var tuple2 = (a: 1, b: 2);

// In C# 7.3, this now works as you would expect.
if (tuple1 == tuple2)
{
    Console.WriteLine("The tuples are equal."); // This will be printed
}

The comparison is done element by element, from left to right. This makes tuples even more useful for simple data structures and for returning multiple values from a method.

2. Enum Constraints in Generics

You can now specify System.Enum as a constraint for a generic type parameter. This allows you to write generic utility methods that work with any enum type in a type-safe way.

// This method will only accept types that are enums.
public static string GetEnumName<T>(T value) where T : System.Enum
{
    return Enum.GetName(typeof(T), value);
}

// Usage
string sunday = GetEnumName(DayOfWeek.Sunday);

3. in Overload Resolution

C# 7.2 introduced the in parameter modifier, which allows you to pass arguments by reference in a read-only manner, avoiding unnecessary copying of large structs. C# 7.3 improves how the compiler resolves method overloads with in parameters, making it easier to work with them without ambiguity.

4. Expression Variables in More Places

C# 7.0 introduced out variables, allowing you to declare a variable right at the point where it is passed as an out argument. C# 7.3 expands where you can use these expression variables, such as in field initializers and query clauses.

Example in a constructor initializer:

public class MyClass
{
    private int _value;

    public MyClass(string s) : this(int.TryParse(s, out var i) ? i : 0)
    {
    }

    public MyClass(int value)
    {
        _value = value;
    }
}

5. Attach Attributes to the Backing Fields of Auto-Properties

Sometimes you need to attach an attribute to the compiler-generated backing field of an auto-implemented property (for example, to control serialization). C# 7.3 introduces a new field: specifier for attributes to make this possible.

public class MyData
{
    [field: NonSerialized]
    public string MyProperty { get; set; }
}

Conclusion

C# 7.3 is a point release focused on developer quality of life and performance. While it doesn't contain any headline-grabbing features, the small improvements to tuples, generics, and in parameters all contribute to making the language more pleasant and efficient to work with. These incremental refinements are a key part of the C# language's continued evolution.