With C# 8 on our doorstep, I wanted to go through some of the C# 6 and 7 language features I have been using that you may have missed.

This is such a small feature but I use it everywhere.

The nameof expression keyword acts like a function, that takes any reference as its argument, and evaluates to the string representing the name of the reference, rather than it's evaluated value. This is a compile-time feature that allows you to represent code as a string but in a referential way. Because this is an expression, and a compile-time behaviour, it can interact with code elements that most other runtime code cannot.

Looking at the problem it solves is a good place to start getting your head around this feature.

var myVar = "This is a variable";
var message = "This message references the name of `myVaar`"; // see typo

// Using the new `nameof` expression
var message2 = String.Format("This message references the name of `{0}`" + nameof(myVar));
// if you used the type `nameof(myVaar)`, the code would not compile

Because of the referential integrity of the argument passed to nameof, you cannot have typos. Code refactoring also treats these as references, and so renaming correctly refactors these references as well.

The nameof expression can be passed variables, Methods, Classes, Properties, pretty much anything. You can even fully qualify nameof(MyNameSpace.MyClass.MyStaticProperty) to correctly generate the string "MyStaticProperty". (Note that only the final part is generated.)

One place I use this is with the Argument Exceptions:

public static void DoStuff(string aValue, int aNumber)
{
    if(aValue == null)
    {
        throw new ArgumentNullException(nameof(aValue));
    }
    
    if(aNumber < 0)
    {
        throw new ArgumentOutOfRangeException(nameof(aNumber), "Must be Positive");
    }

    // Stuff happens here
}

One other place I use this a bunch in Xunit tests. You can use MemberData with Theory tests to programmatically provide data. Reflection is used on a string to locate the Property or Method with the given name. Using nameof here again avoids the risk of typos or failures after renaming things.

[Theory]
[MemberData(nameof(PositiveTestCaseData))]
public async Task WhenIDoStuff_ThenTheRightStuffHappens(string stuff, string expectedOutput)
{
    // ...
}

This is also great for logging in general, so you can make reference to specific fields, types and properties without worrying about names getting out of sync.

One of the smallest but most powerful feature to start using in your code if you haven't already. Hopefully, if you are not using this yourself, this explanation helps you understand when you are reading the code of others who are.