C#6: Using Static Types and Extension Methods

This week I thought I would continue from the last couple of posts on the new language features introduced in C#6 and look at the changes to the `using` keyword.

Up until the latest syntax, `using` was overloaded in three different ways1.

  1. To import types from a specific namespace, reducing the need to fully quality those types when referencing them in subsequent code.
    using System.Collections;
  2. To alias namespaces and types in order to resolve ambiguities when types share a name but different namespaces.
    using Drawing = System.Windows.Drawing; // Namespace alias
    using RectangleShape = System.Windows.Shapes.Rectangle; // Type alias
    
  3. To define a scope at the end of which an object will be disposed
    using (var stream = new MemoryStream())
    {
        // Stuff using the stream
    }

With C#6 comes an additional overload that allows us to import methods from within a specific static class. By specifying the `static` keyword after `using`, we can give the name of a static class containing the members we want to import. Doing this allows us to reference the methods as though they were members of our class.

Using Static

Consider `System.Math`; prior to this updated syntax, using the various methods on the `System.Math` class would require either specifying the fully qualifed type name, `System.Math` or, if `using System` were specified, just the type name, `Math`. Now, by specifying `using static System.Math` we can reference the methods of the `Math` class as though they were members of the class invoking them (without a `System.Math` or `Math` prefix). In this example, `Math.Abs()` is called as just `Abs()`.

using static System.Math;

public class MyClass
{
    public void DoStuff(int value)
    {
        var absoluteValue = Abs(value);
        Console.WriteLine(absoluteValue);
    }
}

As with other additions in C#6, this seems to be aimed at improving developer productivity as it leads to less overall typing when using the methods of a static class. However, the new `using static` syntax also allows for very targeted inclusion of static classes without the rest of their containing namespace, previously only possible with an alias, such as `using Math = System.Math`. This targeting ability, while not really adding anything for regular static methods, makes a significant difference for extension methods.

Extension Methods

As you probably know, extension methods are just fancy static methods, they can even be invoked as would a regular static method. However, extension methods can also be invoekd as though they where member methods of a variable or literal value. For example, both the following examples compile to the same code (assuming we have an enumerable called `list`).

list.Where(x <= 5);
Enumerable.Where(list, x <= 5);

However, before the `using static` syntax, including extension methods was a bit uncontrolled. If you wanted the extension methods in `System.Linq.Enumerable`, you had to include the entire `System.Linq` namespace; there was no way to include only the `Enumerable` static class. In some circumstances, this inability to include just the static class led to annoying type name clashes and occasionally unexpected overload resolution ambiguities or surprises. Now, with `using static` we can specify the exact class of extension methods we want to include and ignore the rest of the containing namespace.

With all that said, there is a notable difference between including regular methods of a static class and extension methods of a static class when importing via `using static <namespace>.<static class>`2.

Subtle Difference

When a static class is imported with `using static`, the way a method can be invoked depends on whether it is an extension method or not. For example, imagine we have a static class called `MyStaticClass` and it has a regular3 static method on it called `Print` that takes a `string`. When included via `using static`, `Print` could be used like this:

void Main()
{
    MyStaticClass.Print("this string");
    // or...
    Print("this string");
}

However, if instead `Print` were an extension method on type `string`, including `MyStaticClass` via `using static` would limit `Print` to being used like this:

void Main()
{
    MyStaticClass.Print("this string");
    // or...
    "this string".Print();
}

Note how in both examples , `Print` can be invoked as a traditional static method when the containing type is referenced, as in `MyStaticClass.Print()`, but their invocation varies when `using static` imports the class. In that second scenario, non-extension static methods are invoked as though they are methods on the current type, where as extension methods are invoked only as though they are methods on a variable. For the extension method version of `Print`, the following is not allowed:

Print("this string");

To use this argument-style syntax with an extension method, we must resort to the same syntax we would have used before `using static`, specifying the type name before the method:

MyStaticClass.Print("this string");

Though I feel it is clear and intuitive, this is a subtle difference worth understanding, as it can lead to breaking changes. Consider if you were refactoring the methods of a static class from extension method to regular static method or vice versa, and that class were imported somewhere with `using static`; any invocations that were not prefixed with the static class name would fail to compile.

In Conclusion

Overall, I like the new `using static` syntax; I believe the differences in method invocation from how static class methods are normally invoked makes sense and I hope you do too. Like all the other features of C#, there will be times to use this feature and times to let it go in favour of something clearer and more appropriate. For me, the ability to pluck a specific class and its extension methods from a namespace without importing the rest of that namespace is the most useful aspect of `using static` and probably what I will use most. How about you? Do you see yourself adding `using static` to your coding arsenal, or is it going to languish in your scrapbook of coding evil? Do tell.

  1. The MSDN docs say two different ways, but it was clearly three and is now three and a halfish []
  2. However, unlike the subtle difference I highlighted in my last post, thankfully, the compiler will catch this one []
  3. as in not an extension method []