Semicolon when used alone represents empty statement.
Also, refer to the answers here - Legal to allow multiple semicolons with nice detailed explanation that why its perfectly legal. Refer to answer by Binary Worrier explained for macros.
From that post -
You can redefine macros to be different to their initial definition, also you can redefine them so they don't exist at all.
Given an assertion macro #define ASSERT(c) if(!c) throw new AssertionFailedException()
you can have your coded littered with ASSERT statements.
void Foo(int x) {
int y = x + 2;
ASSERT(y != 0);
int z = x / y;
. . . .
}
Now consider that you only want the asserts in debug builds, but not in release builds, for release you redefine the macro to be empty (literally #define ASSERT)
. Now when Foo goes to the compiler for a release build, it looks like this
void Foo(int x) {
int y = x + 2;
;
int z = x / y;
. . . .
}
There's now an empty statement where the ASSERT was, because there may or may not be a statement there (depending on build configuration), the compiler needs to be able to handle an empty statement.
Why this convention was kept in C# where there are nothing like C macros, I have no idea, but possibly because it causes little or no harm.
I would guess that multiple ; are elided by the compiler before it
starts parsing code, therefore your unreachable ; is ignored by the
compiler.