When we use a language, often we tend to overlook some of the more obvious constructs or be frustrated by what we think should work. For example, assume we have this simple problem - our function receives a value and based on that value we run through a switch, but we have code that will be run for multiple cases. Explaining what I mean in words is tough - so let's look at what I'm trying to say in code. Assume we have these enums:
[Flags]
enum Foo
{
NoFoo = 0,
FooA = 1,
FooB = 2,
FooC = 4
}
enum Bar
{
A,
B,
C
}
We want a function that will take in a Bar, and based on that create a Foo. If Bar is A, the the resulting Foo is a combination of FooA, FooB and FooC. If Bar is B, then it's a combination of FooB and FooC. If Bar is C, then the result is just FooC. In C, we'd just do this:
Foo FooBar(Bar bar)
{
Foo foo = Foo.NoFoo;
switch (bar)
{
case Bar.A:
foo |= Foo.FooA;
case Bar.B:
foo |= Foo.FooB;
case Bar.C:
foo |= Foo.FooC;
break;
}
return foo;
}
Letting each case fall into the next intentionally. Yes it's a contrived example, but you get the idea. There are cases when we need to do processing like this (like a project I'm doing right now).
Well C# doesn't like this type of construct - I'm not certain why it's illegal (other than missing breaks are common bugs) - but the compiler will say 'Error: Control cannot fall through from one case label ('case 0:') to another'. So you might code a 'fix' like this:
Foo FooBar(Bar bar)
{
Foo foo = Foo.NoFoo;
switch (bar)
{
case Bar.A:
foo |= Foo.FooA;
break;
case Bar.B:
foo |= Foo.FooA;
foo |= Foo.FooB;
break;
case Bar.C:
foo |= Foo.FooA;
foo |= Foo.FooB;
foo |= Foo.FooC;
break;
}
return foo;
}
Not too bad, but if you have to do more processing than a single line it gets ugly and maintainability goes downhill fast.
The thing to keep in mind in C# is that those case statements are actually labels, so you can use them as such, meaning they are valid goto targets, so this code is perfectly valid:
Foo FooBar3(Bar bar)
{
Foo foo = Foo.NoFoo;
switch (bar)
{
case Bar.A:
foo |= Foo.FooA;
goto case Bar.B;
case Bar.B:
foo |= Foo.FooB;
goto case Bar.C;
case Bar.C:
foo |= Foo.FooC;
break;
}
return foo;
}