August 16, 2023—A short list of concepts and features in C# over which you cannot abstract (programming)
I love abstractions. One of my favorite things about software is finding, making, and using good abstractions.
C# has many wonderful abstractions.
But C# also has concepts and features over which it’s simply not possible to abstract.
(A prior version of this article complained about ref struct, but C# 13 fixed that, hooray!)
Here are some examples of what I mean by “coding by convention”:
foreach loops
You can use it with an instance of any type that satisfies the following conditions:
- A type has the public parameterless
GetEnumeratormethod. Beginning with C# 9.0, theGetEnumeratormethod can be a type’s extension method.- The return type of the
GetEnumeratormethod has the publicCurrentproperty and the public parameterlessMoveNextmethod whose return type isbool.
using statements
You can also use the
usingstatement and declaration with an instance of a ref struct that fits the disposable pattern. That is, it has an instanceDisposemethod, which is accessible, parameterless and has avoidreturn type.
await operator
An expression
tis awaitable if one of the following holds:
tis of compile-time typedynamicthas an accessible instance or extension method calledGetAwaiterwith no parameters and no type parameters, and a return typeAfor which all of the following hold:
Aimplements the interfaceSystem.Runtime.CompilerServices.INotifyCompletion(hereafter known asINotifyCompletionfor brevity)Ahas an accessible, readable instance propertyIsCompletedof typeboolAhas an accessible instance methodGetResultwith no parameters and no type parameters
There are a growing number of things like this in C# where you’ll be able to use this or that feature if you follow a bunch of rules.
But it’s impossible to abstract over all foreach-able types, or all disposable types, or all awaitable types. Because with each of these features C# introduced a convention, and a convention is different than an interface. It doesn’t matter if they also introduced an interface (as is the case with foreach and IEnumerable, or with using and IDisposable)—the fact that they introduced a convention at all means there will be types which are foreach-able that do not implement an interface.
It’s impossible to abstract over all manifestations of any of these concepts.
C# pioneered async/await. And so colored functions are C#’s fault.
Yes, async cancer is a real thing. I’ve experienced it. You are twelve layers deep in a project with 10,000 source code files and discover that you need to call an asynchronous function from a synchronous function. Then you have to waste the rest of the day refactoring the entire application to support async from the top all the way down. And you can only hope that you didn’t break all the completely unrelated things you had to modify just to support this.
You cannot abstract over the color of a function.