7

I'm well aware that C# does not allow readonly fields in switch blocks, which is what this question addresses.

I'd like to understand why this is the case. Is it just an arbitrary language specification quirk, or is there a technical reason behind it, and if so, what is that technical reason?

Let me make it clear that I understand the difference between const and readonly, and I know that C# switch requires const values, or values known at compile time. To me, functionally, using a bunch of if..else if statements has the same outcome as using a switch statement, because whatever I can do with a switch statement I can achieve with an if as well, for example:

const int MyConstantValue = 10; int myCompareValue = 3; if(myCompareValue == MyConstantValue) { //... } else { //... } switch(myCompareValue) { case MyConstantValue: //... break; default: //... break; } 

Both of these constructs have the same outcome: the else or default block is executed, but the if can do it without compile time constants or known values. Why can an if do that where a switch cannot?

4
  • C# switches are modelled after C/C++ switches, which have the same constraint. The reason is for performance: A switch statement can be compiled into a very efficient "jump table", which wouldn't be possible if the cases were not known at compile time. So the answer is "to ensure that a switch statement is performant". Another point is that a switch statement has provably unique cases at compile time, but without constant cases that would not be provable at compile time. Commented Jan 23, 2017 at 9:32
  • @MatthewWatson that is what I was looking for, post your comment as an answer and I will accept it. Commented Jan 23, 2017 at 9:36
  • Your misunderstanding starts with What I am not understanding is that functionally, using a bunch of if..else if statements has the same outcome. It is not! Read Matthews comment. Commented Jan 23, 2017 at 9:36
  • I'm not saying they are the same thing, I said that they can have the same outcome. How they go about doing that can be different, but the end result is the same. Commented Jan 23, 2017 at 9:39

5 Answers 5

5

The reason for this is that C# switches are modelled after C/C++ switches, which have the same constraint.

There are two reasons for this constraint:

  • Performance: A switch statement can be compiled into a very efficient "jump table", which wouldn't be possible if the cases were not known at compile time.
  • Correctness: A switch statement has provably unique cases at compile time, but without constant cases that would not be provable at compile time.
Sign up to request clarification or add additional context in comments.

Comments

2

Switch case labels can only be compile time constants (at least until C# 7 ships, where switch will also be useful for pattern matching). A readonly field is not a compile time constant, its a field that is initialized when code is executed.

The only difference between a readonly field and a regular field is that the former can only be initialized in a constructor or via a field initializer while the latter can be initialized / reassigned anywhere.

To see the diference, between a readonly field or a constant, simply consider the following code:

bool const True = true; if (!true) throw new Exception(); //Compiler warning: unreachable code detected return; 

Here, the compiler knows what True is at compile time and can therefore reason about the reachability of the throw statement, which is obviously unreachable; if (false) will always ignore the following block / statement.

Now consider the following:

readonly static bool True = true; if (!true) throw new Exception(); return; 

This will compile just fine. The compiler can not know that True is in fact true untill the class containing the field is initialized. This means code has to run and only the runtime can reason about what True really is.

As to why switch case lables need to be constant, I believe its mainly due to optimization reasons (more efficient code) and coverage analysis; the compiler can reason out if all possible cases are handled or not, something very useful in many scenarios and not possible with non constant labels.

If you need to branch based on non constant expressions then simply use if else if else.

Comments

2

As this piece of documentation states, readonly fields are a runtime constant. (As opposed to const fields).

Meanwhile, switch statements expect an initialized value in compile-time.

Therefore, the compiler rejects the readonly fields.

Edit: Not an expert on the C# compiler, but it appears that it creates a branch table in the IL, as opposed to if-statements (in certain cases). For more information, check out this blog post.

To quote the relevant part:

...first time the function is called a Dictionary of strings (key) and int (value) is created and all the cases are stored in this dictionary as the key and an integer as a value is stored against it. Then for the switch statement the string is taken and is queried in the dictionary and if it is present the number value for the string is returned. Using this number the compiler creates an efficient jump table and it jumps to the target Console.Writeline string.

Now the answer :). The strings are pre-stored in the dictionary. If the strings in the case statement were not constants, changes in them won’t reflect in the dictionary and hence you’d land up comparing against stale value. To avoid this inconsistency the non-constant values are not supported at all.

Obviously for dynamic values the dictionary cannot be used and hence there is no optimization possible for switch-case so one should anyway use if-then-else.

6 Comments

This doesn't answer why the switch statement requires compile time constants. This answer is exactly what is discussed in the question I linked in my question.
To clarify: readonly variables are set from the constructor. const variables are set on compile-time.
@Logan that is not your question.
@PatrickHofman that is my question. I'd like to understand why this is the case. Is it just an arbitrary language specification quirk, or is there a technical reason behind it?
Because the compiler needs constants to generate the code tree to evaluate a switch. That is what nikovn explained @Logan
|
0

Think about what the compiler does with a switch block: it essentially builds a series of conditional branches, based on the target values of the relevant expression, which are known at compile time.

Now suppose you had this:

private readonly int N = System.DateTime.Ticks; 

If you try to use N as the target value of a switch block, then the value is not known at compile time. So what should the compiler do? Although it could of course build a more-complex series of conditional branches, would that really be justified by the "utility" of this pattern?

Personally, there are other things I'd rather the compiler writers spent their time on...

Comments

-1

Its because readonly variables are not constant. They can be assigned in the declaration or in the class constructor. Therefor its value is not known at design time.

1 Comment

This doesn't answer my question of why this is the case.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.