grrussel
grrussel

Reputation: 7339

Can C# Provide a static_assert?

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.

Upvotes: 21

Views: 14058

Answers (7)

DarthGizka
DarthGizka

Reputation: 4675

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.

Upvotes: 12

Paul Groke
Paul Groke

Reputation: 6447

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.

Upvotes: 7

Komat
Komat

Reputation: 325

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.

Upvotes: 2

Mehrdad Afshari
Mehrdad Afshari

Reputation: 422162

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

Upvotes: 4

Thorarin
Thorarin

Reputation: 48496

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.

Upvotes: 1

zihotki
zihotki

Reputation: 5191

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.

Upvotes: 1

Tobias Langner
Tobias Langner

Reputation: 10828

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.

Upvotes: -1

Related Questions