Chapter 2
Statements
C# provides us with a countless
number of statements. Let us now understand each one of them in detail.
a.cs
public class zzz
{
public static void Main()
{
bool i = false;
if ( i)
int j = 40;
}
}
Compiler Errors
a.cs(7,1): error CS1023: Embedded statement cannot be a
declaration or labeled statement
It is not mandatory for the if
statement to have the {} if we want to limit its scope to only one line.
However, we cannot have a variable declaration as the only line following it.
This is because if the condition results to false, the variable will never be
created.
The value of the variable i can
only be determined at run time and thus C# flags an error. The line following
the if condition, int j = 40; is called
an embedded statement.
Every C# statement has an
end-point. The end-point is intuitively the end of the statement. If a
statement can be executed, we call it a reachable statement else it is
unreachable. Unreachable statements for some reason flag a warning and not an
error. To figure out whether a statement is reachable or not, C# does flow
analysis.
a.cs
public class zzz
{
public static void Main()
{
int i = 30;
const int j = 30;
if ( i == 3)
System.Console.WriteLine("hi");
if ( j == 3)
System.Console.WriteLine("hi");
}
}
Compiler Warning
a.cs(10,1): warning CS0162: Unreachable code detected
Two identical if statements. One
produces unreachable code whereas the second does not. In the case of variable
j, the compiler sees it as a const and therefore knows that its value will
never change. The if will always end in false, therefore, a warning.
In the case of the first if, even
though the value of variable i is 30 and C# knows that it results in false, it
still believes that an act of God can change the value of variable i.
Consequently, no error but a mere warning is generated
As for the value of a const
variable, even God possesses no privilege to change it, hence the compiler pops
up a warning. The above rule is part of what goes under the moniker of flow
analysis. An end point should not be reached for a switch and a return
statement.
The if statement falls under the
set of selection statement. It executes statements depending upon the value of
a controlling expression.
if (x) if (y) F(); else G();
if (x) {
if (y) {
F();
}
else {
G();
}
}
This block of if statements is
copied straight from the documentation to denote an idea. The above two ifs are
the same. What we want to highlight here is that the brackets are optional; the
visual look and feel can get misleading at times.
Switch
Statement
a.cs
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.abc(1);
a.abc(10);
}
void abc(int i)
{
switch (i)
{
case 0:
System.Console.WriteLine("zero");
break;
case 1:
System.Console.WriteLine("one");
break;
default:
System.Console.WriteLine("end");
break;
}
}
}
Output
one
end
A switch statement is a
substitute to multiple ifs as both work in the same fashion. It is a matter of
style in deciding on the use of them. We use the time of day to decide which
statement to use when and where. In other words, alike if, the switch also
brings intelligence to our programs.
The switch statement checks the
value of the variable i against the values given with the case statement. If
any one value matches, all the statements within the case upto the break
statement are executed. If none of the case statements match, the default
statement is executed. Remember it is not mandatory to have a default statement
with a break. The value of the variable in question decides on the code to be
executed.
a.cs
public class zzz
{
public static void Main()
{
}
void abc(int i)
{
switch (i)
{
case 0:
System.Console.WriteLine("zero");
case 1:
System.Console.WriteLine("one");
break;
}
}
}
Compiler Error
a.cs(8,1): error CS0163: Control cannot fall through from one
case label ('case 0:') to another
The case statement has its own
set of complex rules. The first one says that every case must end with a break
statement; thereupon preventing it from encroaching into the next case. Don't
we have railing to prevent us from falling over our balcony? Thus, there is no
natural way invented to execute code within two case statements at the same
time. Most people consider it to be an unnecessary restriction C# has placed on
the usage of the case statement. Like everything in life, there is a
workaround. But we will elaborate on that later.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.abc(0);
}
void abc(int i)
{
switch (i)
{
case 0:
System.Console.WriteLine("second");
goto case 1;
case 1:
System.Console.WriteLine("first");
break;
}
}
}
Output
second
first
We explained that one case could
not fall through another case but if it is imperative then you can use the goto
statement. This statement accepts a case statement as a parameter and then
simply jumps to it. Thus, after executing the code of one case, we can jump to
another case and execute its code.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.abc(0);
}
void abc(int i)
{
switch (i)
{
case 0:
System.Console.WriteLine("second");
goto case 1;
System.Console.WriteLine("hi");
case 1:
System.Console.WriteLine("first");
break;
}
}
}
Compiler Warning
a.cs(15,1): warning CS0162: Unreachable code detected
Output
second
first
Here we have confused the
compiler to no end and we advise you not to write such code. At first, the
compiler informs you that no lines of code after a goto will ever be called and
thus generates an unreachable code warning.
But then the control seeps to the
case statement directed by goto and forgets that it was falling through a case
statement from another. We are not trying to belittle the compiler but can it
not please remember goto statements that we write and stop looking stupid in
front of our eyes.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.abc(0);
z.abc(3);
z.abc(1);
}
void abc(int i)
{
switch (i)
{
case 0:
System.Console.WriteLine("zero");
goto case 3;
case 1:
System.Console.WriteLine("one");
goto default;
case 3:
System.Console.WriteLine("two");
break;
default:
System.Console.WriteLine("last");
break;
}
}
}
Output
zero
two
two
one
last
To prevent a fall through, every
case has to end either with a goto or a break. With a break statement, no other code gets called but with a goto, the
code to be executed depends entirely on the case it jumps to. The above code is
the ideal way to write code. However…
a.cs
public class zzz
{
public static void Main()
{
}
void abc(int i)
{
switch (i)
{
case 0:
while ( true) ;
case 1:
return ;
case 3:
throw new System.Exception();
}
}
}
The documentation very clearly
states that the end point of the case statement must not be reached. Thus, any
lines of code that prevent the last line of the case being executed is legal
tender. A while (true) goes on forever, This make sure that you do not leave
the while forever and thus the endpoint of the case never gets reached. Though
it does not make any sense at all as i being zero will enter the while and
never leave the case.
A return is more logical at this
point as it can exit the function altogether and not just the case. It may be
what you had in mind, however. A throw statement also breaks the case, the try
catch is not mandatory here. Just because the compiler does not complain with a
warning or an error it does not mean that you are home scott free.
a.cs
public class zzz
{
public static void Main()
{
}
void abc(int i)
{
switch (i)
{
case 0:
System.Console.WriteLine("zero");
break;
case 0:
System.Console.WriteLine("one");
break;
}
}
}
Compiler Error
a.cs(13,6): error CS0152: The label 'case 0:' already occurs
in this switch statement
Somebody somewhere did some
research and found out that people fell asleep while writing code. When they
woke up, they rewrote the same code again. Thus, we had two copies of the same
case in the program. C# peeks into human mind and is aware that you'd fall
asleep at the wheel.
Thus, it checks whether you are
writing the same code all over again. If it catches you in the act, you will
see an error like the one above. It makes no logical sense having two case
statements with the same value. If the value of i happens to be zero then there
is confusion as in which case should C# execute. The first, the second, or
both, or, even better, none. Decisions, decisions, they are so difficult to
make, so the C# compiler drops by an error.
a.cs
public class zzz
{
int j = 10;
public static void Main()
{
}
void abc(int i)
{
switch (i)
{
case j:
break;
case j > 4:
System.Console.WriteLine("zero");
break;
}
}
}
Compiler Error
a.cs(11,6): error CS0150: A constant value is expected
a.cs(13,6): error CS0029: Cannot implicitly convert type
'bool' to 'int'
Here we want to convince you that
an if statement in our humble/esteem opinion is better than a switch statement.
In a case statement, we have to check the variable with a constant value. We
are not permitted to use a variable or a logical condition. They have to be
constant predefined values only. This limits our flexibility in writing complex
conditions thereby preventing us from writing more abstract more intelligent
code.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.abc(7);
z.abc(70);
}
void abc(int i)
{
switch (i > 8)
{
case 4 > 5:
System.Console.WriteLine("first");
break;
case 4 < 5:
System.Console.WriteLine("second");
break;
}
}
}
Output
first
second
We could not for the life of us
figure out, why the above program works. So we added the following lines at the
end of the second case statement ergo producing an error.
case 40 > 5:
System.Console.WriteLine("third");
break;
and we got the following error
Compiler Error
a.cs(19,6): error CS0152: The label 'case 1:' already occurs
in this switch statement
z.abc(7) initializes i to 7,
hence the switch condition becomes
false. The case may show logical operations but finally has either 1 or 0
standing for true or false. As the switch results in false, the first case is
executed where case has a value of 0. In the second case, z.abc(70), the switch
is true and thus the second case that evaluates to true gets called.
In one of the earlier examples,
we demonstrated that a duplicate case value was not admissible. The error
delivers the same message. In the above case, the compiler should take up the
Hitlerian attitude and erase all our code; to punish us for writing such
gibberish.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
yyy a = new yyy();
z.abc(a);
}
void abc(yyy i)
{
switch (i)
{
case 0:
System.Console.WriteLine("second");
break;
case 1:
System.Console.WriteLine("first");
break;
}
}
}
class yyy
{
}
Compiler Error
a.cs(11,9): error CS0151: A value of an integral type expected
The C# compiler informs us that a
switch statement can only take certain integral types. To be more specific, it
can take an sbyte, byte, short, ushort, int, uint, long, ulong, char, string,
or an enum-type. C# realizes that the switch has received a yyy object and
since it cannot convert a yyy into the above integral types, it reports an
error.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
yyy a = new yyy();
z.abc(a);
}
void abc(yyy i)
{
switch (i)
{
case 0:
System.Console.WriteLine("second");
break;
case 1:
System.Console.WriteLine("first");
break;
}
}
}
class yyy
{
public static implicit operator int ( yyy a)
{
System.Console.WriteLine("operator");
return 1;
} }
Output
operator
first
The only way to eliminate the
error is by overloading an int operator with class yyy. This will convert the
yyy object into an int. As the function returns the resultant int as 1, the
second case will be called. In a real life scenario, the return value depends
upon some instance variable. Thus each time the switch statement will call the
int operator to convert the yyy object into an int and then call the case
statements depending on the resultant value.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
yyy a = new yyy();
z.abc(a);
}
void abc(yyy i)
{
switch (i)
{
case 0:
System.Console.WriteLine("second");
break;
case 1:
System.Console.WriteLine("first");
break;
}
}
}
class yyy
{
public static implicit operator int ( yyy a)
{
System.Console.WriteLine("operator");
return 1;
}
public static implicit operator byte ( yyy a)
{
System.Console.WriteLine("operator");
return 1;
}
}
Compiler Error
a.cs(11,9): error CS0151: A value of an integral type expected
We really got off the wrong side
of our bed today. We therefore added two operators, one that converts a yyy to
an int and the other that converts a yyy to a byte. As the case has no
preferences of a byte over a int, it gets confused and does not know which
operator to call. Since both are equally probable, it flags an error. The
switch expression decides on the governing type of the statement.
There are many more rules to
switch that sensibly state that there can be no two defaults in a switch. The
end-point, that is, the end of the switch must never be reachable or else it
will result in a compile time error.
a.cs
public class zzz
{
public static void Main()
{
zzz z = new zzz();
z.abc(2);
z.abc(3);
}
void abc(int i)
{
switch (i)
{
case 0:
System.Console.WriteLine("first");
break;
case 3:
case 2:
System.Console.WriteLine("second");
break;
}
}
}
Output
second
second
You can have many case statements
bunched up together. There will be no errors if you do so. In the above switch,
for the values 2 and 3, the same code is executed. Thus multiple labels are
permitted in the switch. case 2: can be replaced by a default: and it would
still result in the same answer. Though it is not a good programming style as
removing the case 2: would not change the answer and default means none of the
above. The order of the case statements is irrelevant to the final answer so
changing the order doesn't change anything. For that matter, the default came
first for all that C# cares.
The governing type of a switch
can also be a string. The case statement values are however case sensitive. We
can also use null as we are talking real objects here.
Within a case statement we can
use declaration statements i.e. create variables etc.
a.cs
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.abc(0);
}
void abc(int i)
{
switch (i)
{
case 0:
int j= 7;
System.Console.WriteLine(j);
break;
}
}
}
Output
7
a.cs
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.abc(0);
}
void abc(int i)
{
switch (i)
{
int j= 7;
case 0:
int k= 7;
break;
case 1:
System.Console.WriteLine(k);
break;
}
}
}
Compiler Error
a.cs(12,1): error CS1523: The keyword case or default must
precede code in switch block
For some reason, we are not
allowed to create variables in the switch statement. Had we been allowed, then
the variable would have scope in the entire switch. As we are allowed to create
them only in a case, their scope is restricted to that case only. In the above
program, remove the line int j = 7 and relish the error as seen below.
Compiler Error
a.cs(16,26): error CS0165: Use of unassigned local variable
'k'
Empty
A lot of life goes by without
doing anything. As shown in one of the earlier examples, the while loop did
absolutely nothing. Whenever a statement does nothing we call it an empty
statement. Perfectly valid in C#. Execution of an empty statement simply
transfers control to the statement's end point.
a.cs
public class zzz
{
public static void Main()
{
}
void abc(int i)
{
goto aa;
aa:
}
}
Compiler Error
a.cs(10,1): error CS1525: Invalid expression term '}'
a.cs(11,1): error CS1002: ; expected
We normally create labels at the
end of a function so that all code can jump to the end if they so choose to.
Here we have created a label aa on the last line of function abc. We get an
error as C# demands at least one statement following the label.
C# demands a statement so we have
no choice but to give it an empty statement. The best way to cut short an
argument is by doing what C# wants and give it a statement, albeit, an empty
one, one that does nothing.
a.cs
public class zzz {
public static void Main()
{
}
void abc(int i)
{
goto aa;
aa: ;
}
}
Just by placing a semicolon, we
bring a big smile on the C# virtual face. ;)
Loops
In all, we have four iterative
statements, the for, foreach, while and the last kid on the block, the do
while.
a.cs
public class zzz {
public static void Main() {
int i = 1;
do
{
System.Console.Write(i + " ");
i++;
} while ( i <= 3);
}
}
Output
1 2 3
Earlier we learnt about the
differences between the for and the while and the scientific reasons behind
choosing one of them. Now we add a twist and introduce the do while. This is
identical to the while. There is just one and only one difference between them.
Minor but could be life-threatening at times. In a while, the condition is
evaluated at the beginning of the loop whereas in the case of the do while it
is at the end.
a.cs
public class zzz {
public static void Main() {
int i = 1;
do
{
System.Console.Write(i + " ");
i++;
} while ( i <= 0);
i = 1;
while ( i <= 0 ) {
System.Console.Write(i + "...");
i++;
}
}
}
Output
1
In the do statement, the loop
condition is checked at the end of the first iteration. Thus, the loop is
executed at least once. In the above case, the condition i <= 0 is false at
the outset as the variable i is given a value of 1. In spite of this, the do
executes once, the condition is patently false and we quit out.
For the while, the condition is
checked at the very beginning and thus as the condition is false, the code in
the while is never called.
a.cs
public class zzz
{
public static void Main()
{
for ( int i = 1; i <= 10 ; i++)
System.Console.Write(i + "...");
System.Console.Write(i);
}
}
Compiler Error
a.cs(7,22): error CS0103: The name 'i' does not exist in the
class or namespace 'zzz'
Any variable created in the for
has a lifetime or scope within the embedded statements only. The variable i
does not exist beyond the for statements.
a.cs
public class zzz
{
public static void Main()
{
for ( int i = 1; ; i++)
System.Console.Write(i + "...");
}
}
Please, please do not run the
above program as it will never ever stop unless you Ctrl-C it. The for
condition if present must result in a boolean. Having a numeric data type in
between the two semi colons would raise the red flag for the compiler and
result in an error. If the boolean expression is omitted, then C# assumes the
value to be true by default. Hence the above code never ever stops.
a.cs
public class zzz
{
public static void Main()
{
int [] a = new int[3]{1,2,3};
foreach ( int i in a)
{
System.Console.Write(i + " ");
i = 10;
zzz.abc(ref i);
System.Console.WriteLine(i);
}
}
public static void abc(ref int j)
{
System.Console.Write("abc " + j + " ");
j = 100;
}
}
Compiler Error
a.cs(9,1): error CS1604: Cannot assign to 'i' because it is
read-only
a.cs(10,13): error CS1605: Cannot pass 'i' as a ref or out
argument because it is read-only
We are not allowed to change the iteration variable nor pass it
as a ref or out parameter. We are doing both in the above program, hence the
error.
Jump
Statements
Jump statements transfer control
to another part of our program. This transfer takes place come hell, hath or
fury. In other words, the transfer is unconditional. We have exactly five
conditional statements namely break, continue, goto, return and throw. The
statements that a jump takes you to, is called the target of the jump.
The break statement was touched
upon in the switch statement where it exits out of the case. It works in a
similar fashion for a while, for, foreach and do while. It simply exits out of
the looping construct it is befitted in.
a.cs
public class zzz
{
public static void Main()
{
break;
}
}
Compiler Error
a.cs(5,1): error CS0139: No enclosing loop out of which to
break or continue
You cannot have a break statement
by itself. It has to be placed within a case or a loop construct.
a.cs
public class zzz {
public static void Main() {
int i = 1, j = 1;
for ( i = 1; i<= 10 ; i++) {
if ( i == 3)
break;
for ( j = 1; j <= 10; j++)
{
System.Console.WriteLine(i + "." + j);
if ( j == 1)
break;
}
}
System.Console.WriteLine(i + " " + j);
}
}
Output
1.1
2.1
3 1
The number of breaks introduced
in the program must be equal to the loops statements. As all of us are very
particular about money and will never forget who we lent it to, similarly C# is
very finicky about breaks and hates it hanging around in the program code.
The inner break makes sure that
the inner for executes only once and not 10 times. It ends the loop each time
the value of j reaches 1. The outer break lets us prematurely exit the outer
for loop when the value of i is 3. As we love pairing up people, C# pairs up
breaks with loops.
a.cs
public class zzz
{
public static void Main()
{
int i = 1, j = 1;
for ( i = 1; i<= 10 ; i++)
{
if ( i == 3)
break;
for ( j = 1; j <= 10; j++)
{
System.Console.WriteLine(i + "." + j);
if ( j == 1)
goto aa ;
}
}
aa:
System.Console.WriteLine(i + " " + j);
}
}
Output
1.1
1 1
There is no known way to quit out
of nested breaks in this version of C#. You may have to wait till the next
version and even then, no guarantees. The only alternative here is a goto
statement; this we have shown in the above program. These days the goto statement
has fallen in disrepute. No programmer worth his/her salt will admit in public
that they have ever used the goto statement. Most respectable programming
schools will refuse to teach it. Your programming mother will wash your mouth
with soap if she finds out that you have been reading all about goto.
a.cs
public class zzz {
public static void Main() {
int i = 1;
for ( i = 1; i<= 10 ; i++)
{
try
{
System.Console.WriteLine("1 try");
try
{
System.Console.WriteLine("2
try");
break;
}
finally
{
System.Console.WriteLine("2
finally");
}
}
finally
{
System.Console.WriteLine("1 finally");
}
}
System.Console.WriteLine(i);
}
}
Output
1 try
2 try
2 finally
1 finally
1
The for loop starts and enters
the first try, Here it comes across another try and then when least expected
encounters a break. It would love to get out of the for statement but it stops
to think for a while. The try statement has a finally clause. Hence before it
exits the try, it executes the code in the finally. Now it remembers that there
is one more try statement in the outstanding. This try also has a finally
clause. Once the code in finally gets executed, C# exits from the for loop.
Thus, if the break is nested
among 20 finally clauses, all of them will first get executed and only then
will the break perform its task. Replace the break with a goto aa: statement
and the above explanation will still hold true.
a.cs
public class zzz
{
public static void Main()
{
int i = 1;
for ( i = 1; i<= 10 ; i++)
{
goto aa;
}
{
aa:
System.Console.WriteLine(i);
}
}
}
Compiler Error
a.cs(8,1): error CS0159: No such label 'aa' within the scope
of the goto statement
Compiler Warning
a.cs(11,1): warning CS0164: This label has not been referenced
You can never transfer control
from one block to another block. The goto will allow you to leave a block but
never let you enter another block. This results in the error shown above. The
warning like every warning is at times best ignored.
a.cs
public class zzz
{
public static void Main()
{
goto aa;
{
aa:;
}
}
}
Compiler Error
a.cs(5,1): error CS0159: No such label 'aa' within the scope
of the goto statement
Compiler Warning
a.cs(7,1): warning CS0162: Unreachable code detected
a.cs(7,1): warning CS0164: This label has not been referenced
The compiler is very picky about
you not entering a block. For those who came in late, a block is nothing but
valid C# code in open and close brackets { }. Remember, come heaven or earth,
you cannot enter a block. Add the above to one more mysteries in life that has
no rational answer.
a.cs
public class zzz
{
public static void Main()
{
for ( int i = 1; i<= 10 ; i++)
{
try
{
}
finally
{
break;
return ;
}
}
}
}
Compiler Error
a.cs(12,1): error CS0157: Control cannot leave the body of a
finally clause
Compiler Warning
a.cs(13,1): warning CS0162: Unreachable code detected
Compiler Error
a.cs(13,1): error CS0157: Control cannot leave the body of a
finally clause
You cannot quit out of a finally
block with a break or return statement. If you have a short-lived memory, we
will repeat and tell you that the finally clause of a try is always called. You
cannot leave a finally abruptly. All code in finally must be executed.
We also heed to the warning that
no code after a break ever is executed. In other words, the end point of a
break will never be reached. If it does so, some people tell us that it signals
the end of the world.
a.cs
public class zzz
{
public static void Main()
{
for ( int i = 1; i<= 3; i++)
{
System.Console.WriteLine("C" + i);
continue;
System.Console.WriteLine("A" + i);
}
}
}
Output
C1
C2
C3
The continue statement stops
executing the balance statements and restarts a new iteration of the loop. It
can be used in a while, do, foreach and obviously the for. The rules applied to
break, like having a trillion of them, hold true here also. You can try the
earlier programs of try and finally with continue in place of break.
Similar to break, you cannot
place continue in a finally block. A break says bye-bye to a loop whereas a
continue restarts from the first line of the for loop. Continue is used when
for some unknown reason, the programmer doesn't want to quit the loop but wants
to skip the remaining code in the loop.
It is then assumed that the code
does not exist from the continue keyword to the end point of the loop and the
control moves to the beginning of the loop. Identical to a break, if you ever
write any code after continue, C# will not execute it for a million years.
After that if the sun is yet left standing, it will then execute your code.
Most of what has been repeated earlier also applies to the return keyword.
A goto abides by all rules of
break and continue.
a.cs
public class zzz
{
public static void Main()
{
int aa = 10;
goto aa ;
aa: ;
}
}
C# stores the name of a label and
the name of a variable in different sections of the program. As they belong to
different namespaces, the above program does not generate any errors. Had C#
stored the name of a function and name of a variable in a different namespaces,
we could use the same name for both of them too.
A good programming language
design looks at different namespaces it requires and the contents within them.
The teeming millions like us then abide by their decision and pontificate on
its merits and demerits. We however are powerless to change any of their
decisions. The idea here is that you understand a little in English and then
apply that knowledge to a large number of C# statements.
The checked and unchecked
statements are similar to the corresponding operators except that these
statements act on blocks. The lock statement applies to Threads.
The
Other Odds and Ends
a.cs
class zzz
{
public static void Main()
{
aa: ;
aa: ;
}
}
Compiler Errors
a.cs(7,1): error CS0140: The label 'aa' is a duplicate
Nothing, read my lips, Nothing
can ever be a duplicate. Not even a label.
a.cs
class zzz {
public static void Main()
{
goto case 5 ;
}
}
Compiler Errors
a.cs(5,1): error CS0153: A goto case is only valid inside a
switch statement
A goto case statement only fits
in a case statement. It cannot be used outside a case.
a.cs
class yyy
{
}
class zzz
{
public static void Main()
{
la:
;
{
la:
;
goto la;
}
}
}
Compiler Error
a.cs(13,1): error CS0158: The label 'la' shadows another label
by the same name in a contained scope
Like variables, we cannot have
two labels with the same name shadowing each other. If we use common sense then
the goto would jump to the inner label. What makes sense to us is not what
makes sense to the compiler. If we remove the goto statement, then the error
too disappears.
a.cs
class zzz {
public static void Main()
{
if ( true) ;
}
}
Compiler Warning
a.cs(5,12): warning CS0642: Possible mistaken null statement
The compiler does not like
nothingness. A semi colon by itself stands for a valid statement but at the
same time is an empty statement. If we use a semi colon only where a statement
is due, the compiler gently prods us with a warning.
a.cs
class zzz
{
public static void Main()
{
int i = 6;
switch(i)
{
}
}
}
Compiler Warning
a.cs(8,1): warning CS1522: Empty switch block
Empty vessels make the most noise. The compiler does not understand anything empty including a switch statement. Relax, it is only a warning and can be ignored!