18

I am looking for a way to have compile time assertions in the C# programming language, such as those provided by the BOOST library for C++, or the new C++0x standard.

My question is twofold; can this be achieved in standard, portable C#; Alternatively, can the behaviour be achieved via non-portable assumptions of the quirks of a given C# compiler?

A quick search on google revealed the following link to one technique, whose standards compliance and compatibility I am unsure of.

grrussel
  • 7,209
  • 8
  • 51
  • 71
  • There is just no need for a static_assert in C#. – Massimiliano Jun 08 '09 at 09:31
  • 29
    I disagree, a valuable usecase is verifying correctness properties of compile time computations without having to run the program. – grrussel Jun 08 '09 at 09:48
  • 1
    I also disagree. One of the values of static assertions is that every code path is hit, while if you run the program, only the functionality you happen to hit will get exercised. In a non-trivial program, that's a tiny slice of functionality. – R. Barzell May 29 '22 at 15:35

7 Answers7

9

The approach is the same as in other languages that don't have builtin static asserts (Delphi, older C++ and so on): find a mechanism to convert the condition to be asserted into something that the compiler is unhappy about if the condition is false.

For C# one of the easiest mechanisms to exploit is the warning regarding assignment of negative literals/constants to unsigned types. This has already been mentioned in passing somewhere in this discussion (or at least in one of the pages linked from here), but it is worth showing it in its pure form.

Here's an example that guards two constants - MODULUS_32 and MAX_N from being edited to values that violate expectations:

const uint _0 = (ulong)MODULUS_32 * MODULUS_32 == MAX_N ? 0 : -666;

The -666 makes the error message recognisable as being due to a static assert. Using the ternary operator in this context is preferrable to direct computations because this makes it easier to recognise what's going on (negative results in a computation are more likely to be ascribed to a mistake than explicit, deliberate assignment). Naming the constant something like _MAX_N_must_be_the_square_of_MODULUS_32 makes things even more explicit.

This type of 'static assert' halts compilation reliably - it's not just some warning that could get lost should someone tamper with the /warnaserror switch.

In certain scopes - for example, within functions - it can be necessary to suppress the 'unused value' warning via a pragma:

#pragma warning disable 219
const uint _0 = (ulong)MODULUS_32 * MODULUS_32 == MAX_N ? 0 : -666;
#pragma warning restore 219

C# is pretty much like Delphi in that it lacks a preprocessor, which means that the static asserts and their mechanisms cannot be packaged into neat macros - if you want to use them then you have to type all the plumbing right then and there. But just like in Delphi, the advantages of getting the compiler to check things at compile time are more than worth the small effort.

DarthGizka
  • 4,347
  • 1
  • 24
  • 36
  • 3
    Nice hack! With this approach I was able to assert that some anonymous field is the same name as in other class `const ulong _0 = (ulong)((nameof(anonymousType.FieldName) == nameof(SomeClass.FieldName)) ? 0 : -666);` – Mariusz Pawelski Jun 26 '18 at 13:54
6

Inside a function with a return value, the following trick/hack works (at least with Visual Studio 2005, haven't checked on other platforms):

Something Foo()
{
    if (compiletime_const_condition)
    {
        // ...
        return something;
    }

    // no return statement at all
}

It ain't pretty, but it's the best solution I have so far.

Paul Groke
  • 6,259
  • 2
  • 31
  • 32
  • 1
    I have really mixed feelings about this type of hack, but it really seems like the best solution given some specific environmental constraints regarding external code and framework target. Curious what the response is going to be during code review. – Chuu Oct 08 '15 at 19:32
  • 2
    Func static_assert = () => { if (Position.TopLeft == 0) return null; // no return }; – Bruno Martinez Dec 02 '16 at 15:12
  • 3
    Sometime between Visual Studio 2013 and Visual Studio 2017 this trick stopped working, even if the framework target remains the same. Now all code paths must return a value even if they will never be executed. – Chuu Dec 13 '18 at 17:14
  • @Chuu Unfortunately I don't have another solution. – Paul Groke Dec 16 '18 at 17:21
4

Code Contracts will be added to C# 4.0. It's essentially the same idea done in an elegant way.

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • If you can't wait for C#4.0 you could always get the Microsoft Research version of code contracts here: http://research.microsoft.com/en-us/projects/contracts/ – TheMissingLINQ Jun 08 '09 at 13:01
  • 1
    Code contracts are pretty much obsolete nowadays, and never really did what they should have done. See https://github.com/dotnet/runtime/issues/23869#issuecomment-415670905 for details. – atlaste Feb 18 '20 at 20:10
1

Code from the link you've provided will be compiled in the structure similar to this:

byte a;
if (RenderQuality.Low < RenderQuality.Medium)
    a = 0;
else a = -1;

So the compiler will throw an error. Trinary (conditional) operator ?: is nothing more than syntax sugar like '??' operator.
There are no static asserts in c# because there are no templates in it (generics looks similar to templates but there are a lot of difference and they are not as powerful as templates are). Some functionality may be achieved using conditional statements and preprocessor definitions. See this topic for details about this.

zihotki
  • 5,201
  • 22
  • 26
1

You might try to use something like

( 0 / ( condition ? 1 : 0 ) )

ideally in some situation where the compiler needs to evaluate the value for compilation reason (e.g. some attribute controlling the compilation, maybe structure layout).

EDIT: Because the attribute arguments must be constant expressions, I assume that they are always evaluated during compilation and so any attribute might be sufficient.

EDIT2: Alternative would be to create custom attribute (e.g. StaticCheck) taking a boolean and maybe string and tool which would run as post-build event and use reflection to check all those attributes. Not as nice as having it directly supported however more cleaner than the division hack.

Komat
  • 315
  • 1
  • 4
0

Actually, when I change the expression to something that cannot be evaluated (and thus folded) at compile time, I get a different exception:

Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?)

A compiler that does not do folding should generate a similar compilation error. Thus, on such a compiler it would not compile until you take out all your asserts. Not pretty, but it might be preferable to getting surprises at runtime.

Thorarin
  • 47,289
  • 11
  • 75
  • 111
-1

The code is standard C# Code. It will work on any compiler - but not necessarily at compile time. Since the evaluation at compile-time is only possible if the parameters used in the condition are constant I guess the optimization dependes on the compiler vendor / compiler switches.

In C++, static asserts are either part of the standard (C++0x) or templates that need to be evaluated at compile-time, so they can guarantee the assertion.

To test the portability, I'd use different compilers and especially without any optimization, else you might get the exception at program start.

Tobias Langner
  • 10,634
  • 6
  • 46
  • 76