Vous êtes sur la page 1sur 276

243884994.

doc 1 od 276
C sharp The Nuts and Bolts
Contents

Authors

Introduction

1. Concepts Revisited

2. Statements

3. Enums

4. Types

5. Delegates and Events

6. Conversions - Implicit and Explicit

7. Operators

8. Nested Classes
243884994.doc 2 od 276

9. Miscellaneous

10. Compiler Error Messages

Authors

Vijay Mukhi (vmukhi@vsnl.com) is one of the pioneers of the Indian Infotech Industry. For
years, he has been the frst to teach the emerging technologies in India thus ensuring that
India always has people trained in technologies that the world requires. Vijay has written
over 80 books on computers and programming over the last eight years on subjects ranging
from C, C++ (The Odyssey Series) to Animation and Networking to Java to C#. His newly
released book 'C#-The Basics' has covered many aspects of C# programming language in the
most simplifed form. Vijay abhors complexities and hence his books showcase the most
difcult concepts explained through small programs, thereby giving a good understanding.
Microsoft's .Net technologies is what Vijay is now focussing on and he aims at writing
volumes on it.

Sonal Mukhi (svmukhi@yahoo.com) is a freeance !ro"rammer havin" a #ides!read
e$!osure to com!uter techni%ues and an"ua"es. &ona has done "round'reakin" #ork on
various (nternet )echnoo"ies ike *ava+ ,ctive-+ .er+ C/ and more. &he has co0authored a
fe# 'ooks on com!uter !ro"rammin" too.
(ntroduction

This is our second book on C# so we have assumed that you have read our frst book, 'C# -
The Basics'. It contains a lot more on the C# language that we could not cover in the frst
one.

We have read a lot on what Microsoft and others have written, and wherever we liked the
idea, we simply copied it. And because we have picked from so many places, there was just
no way we could mention each and every of them. If you like an idea in this volume, please
do the same. The objective is to spread the C# message!

People often tend to compare C# with Java. It is with reason, and there's nothing wrong in
doing so. All that we would like to say is that C# has everything of Java and more. But very
soon, you could well have Java or some other language that betters C#'s act.

Today, C# is king of the hill, but we don't know how long it will last. If and when the king is
dethroned, we will be there paying our respects to the new king. With a new book, of course.

We are basically teachers, and believe that a book is a simple extension of teaching. So in
our books, we go step-by-step and systematically because that is the way one teaches in
real-life.

Requirements
The software requirements to successfully run all the programs in this book are

1 Operating System - Windows 2000
243884994.doc 3 od 276
1 Internet Explorer 5.5
1 .Net Framework SDK Beta 2 (111 MB)

Internet Explorer 5.5 can be downloaded of the Microsoft site

1 http://www.microsoft.com/windows/ie/download/ie55sp1.htm

The .Net Framework SDK Beta 2 can be downloaded of the Microsoft site

1
http://download.microsoft.com/download/VisualStudioNET/Trial/2.0/W982KMeXP/E
N-US/setup.exe

Alternatively, you can visit the download section at Microsoft
( http://msdn.microsoft.com/downloads/default.asp ) and download the .Net framework
SDK Beta 2 under the Software Development Kit option.

Acknowledgements
We wish to thank a number of people who gave us their support, new ideas and inspiration
while writing this book.

First and foremost, thanks to Manish Jain, BPB Publications for publishing the book.

We are grateful to Shivani Maheshwari for editing the book in a very short span of time.

To Altaf Hemani and Kishore Rohra, for designing the cover.

Thanks to Manish Purohit for putting in late hours, formatting and aligning the text in the
book. Manish has also painstakingly tried out all the programs in the book and updated
them to work under the Beta 2 version of the product.

To Pradeep Mukhi and Shivanand Shetty, who have always been there, as a source of
inspiration and encouragement.

My Mother, Shana Aunty and a long list of friends need a mention here for their patience
and cooperation on this book while it was being written.

Vijay Mukhi
Sonal Mukhi
Chapter 1

Concepts Revisited

Entry Point

The frst function to be called in any C# program is Main. Obviously someone at Microsoft
tossed a coin and as it fell 'heads', they chose Main. Had it been 'tails', one wonders what
the frst function would have been called. As everything starts from Main, it is also called
243884994.doc 4 od 276
the entry point of the program. It is the fountainhead of all knowledge. Incidentally, the C
programming language also calls Main as its frst function for all C-language programs.

We can have as many as four diferent ways to declare Main in our program. They are as
follows:

static void Main() {...}
static int Main() {...}
static void Main(string[] a) {...}
static int Main(string[] args) {...}

The operating system calls Main and waits for it to return a value. This value denotes the
success or failure of the program.

Main can either return a number or result in no return value, that is, void. If it returns an
int, by convention, a value of zero means success and any other value indicates an error. No
international authority can standardize the error numbers, it largely depends on the
programmer himself.

Copy as a command takes two parameters; the source and destination fles. Similarly, a C#
program can also accept command line parameters at runtime. The executable a.exe can be
entered as

>a one two three

a.cs
class zzz
{
public static void Main(string[] a)
{
int i;
for ( i!; i" a.#ength; i$$)
%&ste'.(onsole.)rite#ine(a[i]);
}
}

*utput
one
two
three

one, two, three are called command line parameters. The program accepts them in an array
of strings. As the array is a parameter to the function, we are free to decide on its name.
Every array has a member called Length, which tells us the size of the array. In our case, it
is three. Thus, a[0] will contain the frst word one and not the name of the program, a[1] will
contain two and a[3] - three. Main now behaves like any other function. What we do next
with the command line arguments depends entirely upon us.

a.cs
class zzz
{
public static void Main(string[] a)
{
}
public static int Main()
{
}
243884994.doc 2 od 276
}

(o'piler +rror
a.cs(,-.!)/ error (%!!01/ 2rogra' 3a.e4e3 has 'ore than one entr& point defined/
3zzz.Main(string[])3
a.cs(5-06)/ error (%!!01/ 2rogra' 3a.e4e3 has 'ore than one entr& point defined/ 3zzz.Main()3

You can have one and only one function called Main in any C# program. Even though you
can call it with diferent parameters, with the name changing, Main as a function must be
given only once.

a.cs
class zzz
{
public static void Main(int i)
{
}
}

(o'piler )arning
a.cs(,-.0)/ warning (%!!.7/ 3zzz.Main(int)3 has the wrong signature to be an entr& point

(o'piler +rror
error (%8!!0/ 2rogra' 3a.e4e3 does not have an entr& point defined

Here, the compiler frst displays a warning signaling us that Main has not been created with
the right parameters. The error, following the warning, proclaims that we have forgotten to
create a function called Main. The signature includes the return type only in special cases
as entry point.

a.cs
class zzz
{
public static long Main()
{
return !;
}
}

(o'piler )arning
a.cs(,-.!)/ warning (%!!.7/ 3zzz.Main()3 has the wrong signature to be an entr& point

(o'piler +rror
error (%8!!0/ 2rogra' 3a.e4e3 does not have an entr& point defined

The signature refers to the parameters given to the function plus the return value. Main in
the above program returns 'long', hence we see the error.

a.cs
class zzz
{
public void Main()
{
}
}

(o'piler +rror
error (%8!!0/ 2rogra' 3a.e4e3 does not have an entr& point defined

243884994.doc 6 od 276
The signature also includes modifers like static etc. It just proves the importance of the
function Main and the way it has been defned.

a.cs
class zzz {
public static void Main()
{
}
}
class &&&
{
public static void Main()
{
}
}

(o'piler +rror
a.cs(.-.0)/ error (%!!01/ 2rogra' 3a.e4e3 has 'ore than one entr& point defined/ 3zzz.Main()3
a.cs(7-.0)/ error (%!!01/ 2rogra' 3a.e4e3 has 'ore than one entr& point defined/ 3&&&.Main()3

The error proves the point that you cannot have two diferent classes, zzz and yyy,
containing Main. Only one occurrence of Main is allowed. You always have only one chance
of a lifetime. Ditto with Main.

a.cs
class zzz
{
private static void Main()
{
%&ste'.(onsole.)rite#ine(9hell9);
}
}

*utput
hell

The access modifers are not included within the signature for Main. Even though Main is
made private, it is considered as an entry point function, hence hell gets displayed. This
function is unique and works as a special case.

a.cs
public static void Main() {
%&ste'.(onsole.)rite#ine(9hell9);
}
class zzz
{
}

(o'piler +rror
a.cs(0-08)/ error (%0807/ +4pected class- delegate- enu'- interface- or struct

You cannot create a function outside a class or a structure. This rule has to be strictly
adhered to even for Main.

a.cs
public class zzz
{
public static int Main()
{
243884994.doc 7 od 276
return;
}
}

(o'piler +rror
a.cs(8-0)/ error (%!0.5/ :n ob;ect of a t&pe convertible to 3int3 is re<uired

In this example, the function Main has to return an int and we are not returning any value
with the keyword return. The compiler tries to convert a nothing into an int and hence the
error. This is a generic error that occurs whenever the compiler tries to convert a data type
into another and is not successful.

a.cs
class zzz
{
public static int Main()
{ }
}

(o'piler +rror
a.cs(,-06)/ error (%!050/ 3zzz.Main()3/ not all code paths return a value

The compiler's error messages need the services of an editor. The function Main should
return an int, but we forgot to do so. This results in an error. Thus whenever an entity
should return a value, we should return such a data type or be prepared for an error.
Scope

Scope, as a concept, defnes the region within which a user-defned entity is visible and can
be referred to.

a.cs
public class zzz
{
public static void Main()
{
int i;
zzz i;
} }

(o'piler +rror
a.cs(5-8)/ error (%!0.7/ : local variable na'ed 3i3 is alread& defined in this scope

Twins are always a problem. We cannot have any two entities of the same name as the
compiler will get confused on whether we are referring to the i which is an int or a zzz. Thus
never have duplicate names even though at times they are allowed.

a.cs
class zzz
{
public static void Main()
{
{
int i 7;
}
i 6!;
} }

(o'piler +rror
243884994.doc 8 od 276
a.cs(7-0)/ error (%!0!,/ =he na'e 3i3 does not e4ist in the class or na'espace 3zzz3

A variable is visible from the position it is created to the frst close bracket. As i has been
created within the inner brackets, it is not available to external code thereafter. Hence the
error.

a.cs
class zzz {
public static void Main() {
for ( int i!; i " 0!; i$$)
{
}
i 6!;
}
}

(o'piler +rror
a.cs(5-0)/ error (%!0!,/ =he na'e 3i3 does not e4ist in the class or na'espace 3zzz3

The variable i has been created within the for statement; therefore it can be accessed only
within the brackets pertaining to for. The scope of i is limited to the for {}.

a.cs
class zzz
{
void abc()
{
i 6;
}
public static void Main()
{ }
int i >;
}

The compiler does not read a .cs program in one iteration. If it did so, then the above
program would have resulted in an error as the function abc refers to the variable i which
has not been created for the moment. In the frst round, the C# compiler scans the entire
code. In another pass, it reads the code of abc. As the frst pass had already brought i into
existence, it does not generate any error and compiles successfully. We can use a variable of
a class before it is declared/created provided the variable has been declared before the class
ends.
a.cs
class zzz
{
void abc()
{
i 6;
int i >;
}
public static void Main()
{ }
}

(o'piler +rror
a.cs(8-0)/ error (%!0!,/ =he na'e 3i3 does not e4ist in the class or na'espace 3zzz3

What works for variables in a class does not apply to functions as well. Within abc, we, frst,
initialize i to 9 and on the next line we create it. The compiler is not aware of i's existence
243884994.doc 9 od 276
while parsing the frst line hence it stops and shows an error. Some people are born smart,
well, our compiler is not one of them.

a.cs
class zzz
{
void abc()
{
i 6;
int i >;
}
public static void Main()
{
}
int i 1;
}

(o'piler +rror
a.cs(5-8)/ error (%!0,5/ : local variable na'ed 3i3 cannot be declared in this scope because it
would give a different 'eaning to 3i3- which is alread& used in a 3parent or current3 scope to
denote so'ething else

i as a variable is created in class zzz and is visible to all members in the class. Within the
function abc, we have created another local variable but with the same name as i. If we are
now to refer to i, then are we referring to the local i or the parent i? The local i in abc hides
or prevents any access to the parent i. This turns out to be unacceptable to C# and hence it
reports an error.

a.cs
class zzz
{
public static void Main()
{
zzz a new zzz();
a.abc();
a.p<r();
}
int i 0!!;
void abc()
{
int i 0!;
%&ste'.(onsole.)rite#ine(i);
}
void p<r()
{
%&ste'.(onsole.)rite#ine(i);
i 0;
}
}

*utput
0!
0!!

The program does not generate any compilation error. In function abc, we are allowed to
create a variable named i, similar to the variable i in class zzz. The local variable in abc
hides the parent variable in its function. Initializing i in abc to 10 hence does not change the
value of i in zzz. Also, as there is no variable named i defned in pqr, i will refer to the
243884994.doc 13 od 276
variable and value defned within zzz. i now plays two diferent roles with diferent scopes in
the same class.
a.cs
class zzz
{
public static void Main()
{
int ; ( ; 0) ;
%&ste'.(onsole.)rite#ine(;);
}
}

*utput
0

The compiler gives no error as the variable j is being used on the same line of its creation. It
does not precede it. Had it been like in the previous example, an error would have been
generated. The compiler frst reads the line, creates a variable called j, then initializes it to 1.
The () brackets gain precedence.

a.cs
class zzz
{
public static void Main()
{
int ; 0 - i $$; ;
%&ste'.(onsole.)rite#ine(i);
}
}

*utput
.

A comma works like a semi-colon. The compiler treats it as two diferent statements.

Exceptions

a.cs
class zzz
{
public static void Main()
{
&&& a;
anew &&&();
tr&
{
a.abc();
%&ste'.(onsole.)rite#ine(9?&e9);
}
catch
{
%&ste'.(onsole.)rite#ine(9@n +4ception9);
}
}
}
class &&&
{
public void abc()
{
243884994.doc 11 od 276
throw new %&ste'.+4ception();
}
}

*utput
@n +4ception

When we call a function, we are not aware of the exceptions and the types of exceptions it
will throw. One way to eradicate this uncertainty is to catch all the exceptions. You can do
so by not specifying any Exception with catch. It is perfectly legal tender to unspecify the
name of the Exception we are trying to catch in the catch.

a.cs
class zzz
{
public static void Main()
{
&&& a;
anew &&&();
tr&
{
a.abc();
%&ste'.(onsole.)rite#ine(9?&e9);
}
catch
{
%&ste'.(onsole.)rite#ine(9@n +4ception9);
}
catch (%&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(9@n +4ception9);
}
}
}
class &&&
{
public void abc()
{
throw new %&ste'.+4ception();
}
}

(o'piler +rror
a.cs(05-0)/ error (%0!01/ =r& state'ent alread& has an e'pt& catch blocA

The error message is self-explanatory. The C# compiler does not permit you to have a catch
with no Exceptions followed by a catch with an Exception. The reason being that the frst
catch is more generic and it will catch all the Exceptions. This leaves no exceptions for the
second catch. Reverse the order of the catches and watch the error disappear.

a.cs
class zzz
{
public static void Main()
{
&&& a;
anew &&&();
tr&
{
a.abc();
243884994.doc 12 od 276
%&ste'.(onsole.)rite#ine(9?&e9);
}
catch (444 e)
{
%&ste'.(onsole.)rite#ine(9@n +4ception9 $ e.=o%tring());
}
}
}
class &&&
{
public void abc()
{
throw new 444();
}
}
class 444 / %&ste'.+4ception
{
}

*utput
@n +4ception444/ :n e4ception of t&pe 444 was thrown.
at &&&.abc()
at zzz.Main()

An exception is a class derived from System.Exception. It is child's play to create an
exception. We can create hundreds of Exceptions, there is no certain limit to it. The reason
behind introducing Exceptions was to simplify the job of identifying errors. Earlier if our
function returned six diferent types of errors then the method would return six diferent
numbers. Now, using exceptions it will throw 6 diferent objects. All these objects are derived
from the System.Exception class.

In the program above, xxx is derived from System.Exception and contains no code. Function
abc throws an object that looks like xxx hence the catch traps an xxx object while calling
the function. The output is displayed using the ToString function of the object.

a.cs
class zzz
{
public static void Main()
{
&&& a;
anew &&&();
tr&
{
a.abc();
%&ste'.(onsole.)rite#ine(9?&e9);
}
catch (%&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(9@n +4ception9 $ e.=o%tring());
}
catch (444 e)
{
%&ste'.(onsole.)rite#ine(9@n +4ception9 $ e.=o%tring());
}
}
}
class &&&
{
public void abc()
243884994.doc 13 od 276
{
throw new 444();
}
}
class 444 / %&ste'.+4ception
{
}

(o'piler +rror
a.cs(05-7)/ error (%!05!/ : previous catch clause alread& catches all e4ceptions of this or a super
t&pe (3%&ste'.+4ception3)

System.Exception catches all the exceptions. It is very similar to having a catch with no
Exceptions. It is advisable to make it the last catch in the series of catches as if it is the
frst, it will catch all the exceptions leaving nothing for the other catches. The earlier
explanation holds true in this case too.

Exceptions are said to be a structured, type safe and a uniform way of error handling. All
modern programming languages support exceptions.
a.cs
class zzz
{
public static void Main()
{
&&& a;
anew &&&();
tr&
{
a.abc();
%&ste'.(onsole.)rite#ine(9?&e9);
}
catch (%&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(9@n +4ception9 $ e.=o%tring());
}
finall&
{
%&ste'.(onsole.)rite#ine(9@n finall&9);
}
}
}
class &&&
{
public void abc()
{
throw new %&ste'.+4ception();
}
}

*utput
@n +4ception%&ste'.+4ception/ :n e4ception of t&pe %&ste'.+4ception was thrown.
at &&&.abc()
at zzz.Main()
@n finall&

And if we comment out the throw statement.

*utput
?&e
@n finall&
243884994.doc 14 od 276
Once an exception is thrown, control passes to the catch statements. The statements in try
following the function that causes an exception, are never executed. After executing the
catch code, the next executable statement is the one following the try-catch block.
Irrespective of an exception being thrown or not, the statements following the try catch
blocks are executed. The question here is 'What if we want some code to be called
irrespective of whether an exception occurred a not'. To do so, we simply place the code in
fnally. The code within this block unquestionably is executed. A fnally will always be called
at the end.

a.cs
class zzz
{
public static void Main()
{
int i 8B!;
}
}

(o'piler *utput
a.cs(8-6)/ error (%!!.!/ Civision b& constant zero

The C# compiler is very intelligent. It knows that a number cannot be divided by zero and
thus stops us in our tracks with an error.

a.cs
class zzz {
public static void Main()
{
int ; !;
int i 8B;;
}
}

It is however not very smart and in the above case, an exception is generated at run time.

a.cs
class zzz
{
public static void Main()
{
tr&
{
int ; !;
int i 8B;;
}
catch ( %&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(9Di 9 $ e.=o%tring());
}
}
}

*utput
Di %&ste'.Civide?&Eero+4ception/ :tte'pted to divide b& zero.
at zzz.Main()

All exceptions thrown at run time can be caught using try-catch. In this way, any
unforeseen application error can be caught at run time gracefully.
243884994.doc 12 od 276

a.cs
class zzz {
public static void Main() {
&&& a;
anew &&&();
tr&
{
a.abc();
tr&
{
a.p<r();
}
catch (%&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(9@n inner tr&9 $ e.=o%tring());
}
finall&
{
%&ste'.(onsole.)rite#ine(9@n inner finall&9);
}
%&ste'.(onsole.)rite#ine(9?&e9);
}
catch (%&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(9@n +4ception9 $ e.=o%tring());
}
finall&
{
%&ste'.(onsole.)rite#ine(9@n finall&9 );
}
}
}
class &&&
{
public void abc()
{
BBthrow new %&ste'.+4ception();
}
public void p<r()
{
throw new %&ste'.+4ception();
}
}

*utput
@n inner tr&%&ste'.+4ception/ +4ception of t&pe %&ste'.+4ception was thrown.
at &&&.p<r()
at zzz.Main()
@n inner finall&
?&e
@n finall&

Interesting !!!

Basically, you are allowed to have as many try-catch blocks in your code as your heart's
desire. Every time you execute a function, that function may throw an exception. So you can
place the function call in a try-catch block. Moreover, within a catch block, you can have
another try-catch which should be considered only when there is a function call in the
catch.

243884994.doc 16 od 276
In the program, abc does not throw any exception but pqr does. The catch that catches it is
the inner catch and not the outer catch. Hence, the outer catch is not called. Conversely, the
fnally for both the trys is called, the order is from the inner to the outer resulting into
seeing bye before last. Finally waits for the try-catch to complete its task.

Namespaces

C# programs are organized using namespaces which provide an organizational system for
presenting programs. A source fle is defned as a compilation unit. A C# program can
consist of one or more compilation units. When we try and compile a C# program, all
compilation units are compiled together and hence can depend on each other.

a.cs
public na'espace vi;a&
{
}

(o'piler +rror
a.cs(0-7)/ error (%0807/ +4pected class- delegate- enu'- interface- or struct

A namespace is explicitly public and we are not allowed to specify any access modifers to it.

a.cs
public class zzz
{
public static void Main(string[] a)
{
using %&ste';
}
}

(o'piler +rror
a.cs(8-1)/ error (%0!!,/ %&nta4 error- 3(3 e4pected
a.cs(8-0,)/ error (%0!.5/ ) e4pected

The using directive can only be placed at the beginning of the program. It cannot be used
anywhere else. The scope of using is the current source fle and it does not infuence any
other programs included while compiling.

a.cs
public class zzz
{
public static void Main(string[] a)
{
}
}
na'espace n0
{
class &&&
{
}
}
na'espace n0
{
class &&
{
}
}
243884994.doc 17 od 276

Namespaces are open-ended, the net result here will be that the namespace n1 will contain
both classes -- yy and yyy. However, we cannot change the name of the second class to yyy
as we cannot have the same name in the same namespace.

a.cs
using z a0.a..&&&;
using z0 a0.a.;
public class zzz
{
public static void Main(string[] a)
{
z b;
z0.&&& c;
}
}
na'espace a0.a.
{
class &&& {}
}

Using nested namespaces can make our names too large. We are provided with some help in
the form of an alias in the using directive. In the program, we have created two aliases -- z
and z1. Wherever we write a1.a2.yyy, the full name of the class yyy, it can now be replaced
with z. z1 stands for only the namespace name and thus we have to specify the name of the
class. An alias is conceptually very simple. Wherever C# sees a z, it will blindly replace it by
a1.a2.yyy.

a.cs
using z a0.a..&&&;
public class zzz
{
public static void Main(string[] a)
{
}
}
na'espace a0.a.
{
class &&& {}
}
class z
{
}

(o'piler +rror
a.cs(0-1)/ error (%!815/ Fa'espace 33 alread& contains an alias definition for 3z3

You cannot have a class name and an alias with similar names as this will confuse the
compiler. Hence the error.

a.cs
na'espace a0.a.
{
using z a0.a..&&&;
class &&& {}
}
na'espace a,
{
class ddd / z
243884994.doc 18 od 276
{
}
}

(o'piler +rror
a.cs(7-0,)/ error (%!.>5/ =he t&pe or na'espace na'e 3z3 could not be found (are &ou 'issing a
using directive or an asse'bl& referenceG)

The class zzz remains the same as before. An alias created in one namespace or compilation
unit is simply not available to another. The scope of z is only limited to namespace a1.a2.

a.cs
using z a0.a..&&&;
na'espace a0.a.
{
class &&& {}
}
na'espace a,
{
class ddd / z
{
}
}

Here we do not get any namespace error as we have placed the alias in the global or default
namespace. It is available to all namespaces in this source fle only.

a.cs
using z a0.&&&;
na'espace a,
{
class ddd / z
{
}
}
(o'piler +rror
a.cs(0-00)/ error (%!.>5/ =he t&pe or na'espace na'e 3a03 could not be found (are &ou 'issing a
using directive or an asse'bl& referenceG)
a.cs(0-00)/ error (%!.>5/ =he t&pe or na'espace na'e 3a03 could not be found (are &ou 'issing a
using directive or an asse'bl& referenceG)

C# checks the namespace alias and tries to resolve it to a sensible entity. If the compiler
cannot resolve it, it gives an error. Here C# is not knowledgeable of a1.

a.cs
using z a..a,;
na'espace a..a,
{
}
na'espace a,
{
using z a.;
class ddd / z.&&&
{
}
}

(o'piler +rror
a.cs(7-08)/ error (%!.,>/ =he t&pe or na'espace na'e 3&&&3 does not e4ist in the class or
na'espace 3a.3 (are &ou 'issing a using directive or an asse'bl& referenceG)
243884994.doc 19 od 276

The namespace alias can be hidden by another namespace alias. Globally z is a2.a3 but
within the namespace a3 it is temporarily given a value of a2. Thus the error reads a2.yyy
and not a2.a3.yyy. If we comment the inner alias, the compiler evaluates z to be a2.a3.yyy.

a.cs
na'espace a..a,
{
}
na'espace a,
{
using z a.;
using z0 a..a,;
using z. z.a,;
}

(o'piler +rror
a.cs(7-0.)/ error (%!.>5/ =he t&pe or na'espace na'e 3z3 could not be found (are &ou 'issing a
using directive or an asse'bl& referenceG)

The above error comes in as C# interprets only one using at a time. This means that C#
turns a blind eye to the other using's. The order of usings is of no relevance. We cannot
incorporate an alias within another. All of them have to be self contained with no
dependencies.

a.cs
using a.;
using a,;
class zzz
{
public static void Main()
{
aaa a;
}
}
na'espace a.
{
class aaa {}
}
na'espace a,
{
class aaa {}
}

(o'piler +rror
a.cs(1-0)/ error (%!0!>/ 3aaa3 is an a'biguous reference

The reason we get an error is because in Main, aaa can either become a2.aaa or a3.aaa. As
both namespaces a2 and a3 have a class aaa, both match and hence the error.
Changing using a2 to using a3, will give us only warnings as duplicate usings are not
fagged as an error. The class aaa now becomes a3.aaa and there is no ambiguity.

a.cs
na'espace aa
{
na'espace bb
{
class cc{}
}
243884994.doc 23 od 276
}
na'espace aa.bb
{
class cc{}
}

(o'piler +rror
a.cs(0!-1)/ error (%!0!0/ =he na'espace 3aa.bb3 alread& contains a definition for 3cc3

We cannot have a class cc in namespace aa.bb as it already exists within the namespace bb
under namespace aa. The namespaces are additive, so it fnally becomes aa.bb. The fully
qualifed name for the existing class cc is aa.bb.cc. Thus namespace aa.bb can be viewed as
a namespace aa containing a namespace bb.

Variables

Variables are always stored in memory as they represent a memory location. They cannot be
stored in your or my pockets. The name of a variable is always decided by consulting an
astrologer. This is because you always need someone to take the fak for your programs not
working, so why not blame it on a poor choice of variable names? Remember, in this book,
we decide names of variables, in your life let your loved ones decide!

Every human being has to have some properties associated with him or her. One of these
could be, say, weight. In the same way every variable has to have a data type associated with
it, that informs C# about the values that can be stored in it. C# is very fnicky about what
you store in it, similar to people who are very fnicky about the clothes they wear. The C#
compiler guarantees that the variable will never ever have a value other than what is
permitted by its data type. Big brother is always watching over your shoulder!

Variables in C# fall in two states, initially assigned and initially unassigned. An initially
assigned variable has a defned value, an initially unassigned variable does not. Complex
words but a simple explanation.

If you have been counting, C# has so far created seven types of variables for us. These are:
static, instance, array, parameter types like value, reference and output and fnally local
variables. Let us learn something more about each one of them.

a.cs
public class zzz {
public static int i0;
int i.;
b&te [] i,;
void abc(int i>- ref int i8- out int i5)
{
int i1 ;
i50!;
}
public static void Main()
{
}
}

First, a quick recap. i1 is a static variable due to the keyword static. i2 is an instance
variable as it is created outside a function and i3[0] is an array element if we initialize the
array member using new. Parameters by default are passed by value like i4. Variables like i5
243884994.doc 21 od 276
and i6 are ref and out respectively due to the modifers. Also, as i7 is created within a
function, it is called a local variable.
A static variable is born at the time the class is loaded and is alive and kicking as long as
the program runs. A static variable is initially assigned which means that it gets the default
values of its data type.
All other variables created by themselves in a class are called instance variables. An
instance variable comes to life only when an instance of that class is created using the
keyword new. The instance variable dies when there are no more references to the object. In
languages like C#, like in real life, we have no control over death. Thus, instance variables
die whenever C# feels that it has no use for them. The programmer is powerless to kill an
instance variable.

An instance variable is initially assigned which means that it gets the default values of its
data type. The above line was simple cut and paste from the above paragraph. The only
change made was that the word instance was replaced by static. Good writers can waste
many words writing gibberish. A structure is always created on the stack. Thus, an instance
variable created in a structure, has the same life-time as the structure. If the structure is
initially assigned so are the instance variables and vice versa.

Array members behave in the same way as instance members so we see no point in
explaining the same concept using diferent words and consuming more paper and cutting
more trees. We apologize but had to have the above explanation spread over at least three
lines without repeating ourselves.

A value parameter is assigned a value at the time of function invocation. The variable is alive
at the open brace { and dies at the close brace }. A reference variable unlike a value variable
does not create a new memory location. It simply stands for a reference to the original
object. It represents the original memory location of the object in the function invocation.

Within a structure, the keyword this is passed by reference. A reference variable has to be
assigned a value on invocation unlike the out parameter. The out parameter is similar to the
ref in many aspects. Both have the same value as the underlying variable. We cannot
initialize an out variable at the time of invocation but must initialize it at the time of exit.
Also, as it is initially unassigned, we cannot read its value in the function. The keyword 'this'
in the constructor of a struct is an out parameter.

A local variable is created at the open { and dies at the close }. A local variable is never
initialized and thus has no default value.

a.cs
public class zzz
{
public static void Main()
{
int i;
%&ste'.(onsole.)rite#ine(i);
}
}

(o'piler +rror
a.cs(5-.5)/ error (%!058/ Hse of unassigned local variable 3i3

A local variable is initially unassigned.

243884994.doc 22 od 276
a.cs
public class zzz
{
public static void Main()
{
i 0!;
int i;
}
}

(o'piler +rror
a.cs(8-0)/ error (%!0!,/ =he na'e 3i3 does not e4ist in the class or na'espace 3zzz3

We cannot refer to a variable before it is created and if we do so, the above error is reported.
Had i been an instance variable and created even after Main, it would not have resulted in
an error.

a.cs
public class zzz
{
public static void Main()
{
i 0!;
}
static int i;
}

(o'piler )arning
a.cs(1-0.)/ warning (%!056/ =he private field 3zzz.i3 is never used

All that we get from C# is a benign warning saying that we are not using the variable i. From
the compiler vocabulary, initializing a variable is not similar to using a variable. At times,
the garbage collector responsible for the murder of variables may decide to terminate it
before the close } as it is not being referred to. This is perfectly legal in C#. Thus a variable
may die an earlier death, it is all at the mercy of the great C#.

As a repetition, static, instance and array members all have default values. For a reference
variable, the default value is null.

a.cs
public class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(9.9 $ i $ 9.9);
}
static &&& i;
}
class &&&
{
}

(o'piler )arning
a.cs(1-0.)/ warning (%!5>6/ Iield 3zzz.i3 is never assigned to- and will alwa&s have its default
value null

*utput
..

243884994.doc 23 od 276
i is a variable of a reference type. The warning very clearly informs us that C# has detected a
variable i, which has not been initialized to any value hence it will be null. Null means no
value and cannot be displayed. Null is not even a space and hence the two dots have nothing
separating them.

Arrays

An array is also known as a data structure in formal computer science books. In our view, a
rose by any other name is perceived to smell as sweet no matter what name you call it by. An
array will normally contain more than one variable called the elements of the array. An
array element is accessed using a number in square brackets known by a more difcult
name -- a computed index. Unfortunately, all the members of an array must have the same
data type called the array type. The value we supply to the keyword new in square brackets
is the size of the array or as termed in the array world, a rank specifer.

In Book 1, we have seen only single-dimensional arrays or arrays of rank one. In case of
more than one index, we call it a multi-dimensional array. The array dimension is
represented by a value, which must be larger than or equal to zero. This value is defned as
length and must be positive. It does not have a compile time value but receives a value at
run time.

a.cs
class zzz
{
public static void Main()
{
int [] a;
a new int[];
}
}

(o'piler +rror
a.cs(5-0>)/ error (%0875/ :rra& creation 'ust have arra& size or arra& initializer

During the act of creating an array, the size must be specifed.

a.cs
class zzz
{
public static void Main()
{
int [] a;
a new int[!];
}
}

It is valid or perfectly legal to give a size of 0. The array, however, has no practical value if
you do so.

a.cs
class zzz {
public static void Main()
{
int [] a;
a new int[0!];
a[00] 0!;
243884994.doc 24 od 276
}
}

*utput
+4ception occurred/ %&ste'.@nde4*ut*fJange+4ception/ :n e4ception of t&pe
%&ste'.@nde4*ut*fJange+4ception was thrown.
at zzz.Main()

If you exceed the bounds of an array, you will not get any compile time error but
unfortunately you land up to a run time error. C#, in other words, is extremely concerned
about going beyond the scope of the array. The exception thrown can be caught by your
program.

Once an array has been created, its length cannot be altered any more. We are not allowed
to resize the dimensions. Everything is fxed.

a.cs
class zzz
{
public static void Main()
{
int [] a;
a new int[0!];
a new int[0];
}
}

The frst array remains inaccessible in the program as array a has been redefned as one
element array. Once an array has been created it cannot be resized later.

All arrays are derived from System.Array, which is an abstract class.

a.cs
class zzz
{
public static void Main()
{
string [] a;
a new string[0!];
a[0] 6;
}
}

(o'piler +rror
a.cs(1-7)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3int3 to 3string3

The conversion rules applying to data types also apply to an array.

a.cs
class zzz
{
public static void Main()
{
int [] a new int[,] {!->-7};
int [] b new int[] {!->-7};
int [] c {!->-7};
}
}

243884994.doc 22 od 276
There are many ways to skin a cat. In the same way, we can initialize an array in diferent
ways. The array a has been initialized the long way where we have specifed the keyword new
with the size of the array along with the initial values in {} braces. In the case of b, we've
skipped the length and directly supplied three values. The length of this array becomes 3. In
the third case, we have done away with the new altogether.

a.cs
class zzz
{
public static void Main()
{
int [] a new int[8] {!->-7};
}
}

(o'piler +rror
a.cs(8-.>)/ error (%!017/ @ncorrectl& structured arra& initializer

We cannot create a larger array and supply fewer values in the {} brackets. They have to be
the same. Not one more, not one less.

a.cs
class zzz
{
public static void Main()
{
int[-] b {{!- 0}- {.- ,}- {>- 8}- {5- 1}- {7- 6}};
%&ste'.(onsole.)rite#ine(b.#ength);
foreach ( int i in b)
%&ste'.(onsole.)rite(i $ 9 9);
}
}

*utput
0!
! 0 . , > 8 5 1 7 6

We have created a two dimensional array having a length of fve for the innermost rank and
2 for the outermost rank. It is similar to writing 'b = new int[5,2]' and then initializing the
individual members; b[0, 0]=0, b[0, 1]=1 etc. The member Length in System.Array, inherited
by b displays 10 as the total number of members in the array are 5*2. The foreach construct
then displays every member in the array.

a.cs
class zzz
{
public static void Main()
{
int ; ,;
int[] b new int[;];
%&ste'.(onsole.)rite#ine(b.#ength);
}
}

*utput
,

243884994.doc 26 od 276
Irrespective of what the documentation has to say, you are allowed to use a variable to
initialize an array. We have used the variable j to create b.

int [][,,][,] a, is a three-dimensional array consisting of two-dimensional arrays which in turn
are of single-dimensional arrays each. The value stored in each element will be an integer.

a.cs
class zzz
{
public static void Main()
{
int b[];
}
}

(o'piler +rror
a.cs(8-5)/ error (%!58!/ %&nta4 error- bad arra& declarator. =o declare a 'anaged arra& the ranA
specifier precedes the variable3s identifier
a.cs(8-1)/ error (%08.8/ @nvalid e4pression ter' 3]3
a.cs(8-7)/ error (%0!!,/ %&nta4 error- 3]3 e4pected

The array square brackets must come before the name of the variable and not after. These
are issues of syntax and do not follow any coherent pattern. The above line would be a valid
C syntax.

a.cs
using %&ste';
public class zzz
{
public static void Main(string[] a)
{
b&te[][] s new b&te[8][];
%&ste'.(onsole.)rite#ine(s.#ength);
for (int i !; i " s.#ength; i$$)
{
s[i] new b&te[i$,];
}
for (int i !; i " s.#ength; i$$)
{
(onsole.)rite#ine(9#ength of row {!} is {0}9- i- s[i].#ength);
}
}
}

*utput
8
#ength of row ! is ,
#ength of row 0 is >
#ength of row . is 8
#ength of row , is 5
#ength of row > is 1

A jagged array has a variable number of members. s is a jagged array with 5 members. Each
of these 5 members can be made up of an array of any size. In the frst for, we are initializing
s[0], the frst member, to an array of bytes which is 3 large. In the second iteration of the for,
s[1] now points to an array of bytes having 4 members. Thus, in the second round,
s[1].Length displays 4.

a.cs
243884994.doc 27 od 276
using %&ste';
public class zzz
{
public static void Main(string[] a)
{
int[][] n7 new int[.][] { new int[] {.-,-}- new int[] {8-5-1-7-6} };
int[][] n6 new int[][] { new int[] {.-,->}- new int[] {8-5-1-7} };
}
}

And one more example for the road to ponder on. This shows the levels of complexities that
you can delve in.

a.cs
class zzz
{
public static void Main()
{
int [] a {0-,};
zzz b new zzz();
b.abc(a);
b.abc(new int[]{>-8});
%&ste'.(onsole.)rite#ine(a[0]);
}
public void abc( int [] z)
{
%&ste'.(onsole.)rite#ine(z[0]);
z[0] 0!!;
}
}

*utput
,
8
0!!

You can give an array as a parameter to a function. This can be done in two ways, either by
stating an array name explicitly or by creating an array at the time of creating the function.
There is no way on earth the function can ever know or care on the method you have
adopted. However, an array is passed by reference and any changes made through z will
refect the values in the original array.

In the second case, even though the changes get incorporated, there is no way of accessing
the array, as we never gave it a name. Change the function abc by adding the params
keyword. This is as shown below.

public void abc(para's int [] z)

and the above program runs exactly the same as earlier. This proves that a params keyword
is nothing but an array parameter.

a.cs
class zzz
{
public static void Main()
{
int [] a {0-,};
zzz b new zzz();
b.abc(a);
243884994.doc 28 od 276
b.abc(a-a);
}
public void abc(int [] &- para's int [] z)
{
%&ste'.(onsole.)rite#ine(z.#ength);
}
}

*utput
!
.

There is, however, a major diference between an array and a params parameter. If the
parameter is an array then no other data type can be provided other than an array. Even a
single int has to be passed as an array. However, a params modifer is more fexible as we
are allowed to give individual data types. These are converted into an array. A params
parameter can also be null as in the frst invocation of abc. We can either pass an array or
as many individual ints we desire to the second parameter.

a.cs
class zzz
{
public static void Main()
{
int [] a {0-,};
zzz b new zzz();
b.abc(out a);
%&ste'.(onsole.)rite#ine(a[.]);
}
public void abc(out int [] z)
{
z new int[,]{0->-5};
}
}

*utput
5

With an array, we can also use the modifers of ref and out. The diference between them is
that in the out modifer, we have to recreate an array within the function despite having
initialized it earlier. An out modifers demands new entities to be created inside the function.
If the data type is an array, so be it, you must create it and if we do not initialize it, the value
is automatically initialized to zero. Apart from this distinction, the behaviour followed is like
a normal ref entity. Like any normal object, a variable needs to be initialized before using it
but in case of an out, if the array is not initialized, then it takes the default values of the
data type.

An array can be initialized to a null if need arises as a null may mean no value. This also
means that someone somewhere gave it no value.

a.cs
class zzz
{
public static void Main()
{
int [] a new int[,]{0-.};
}
}
243884994.doc 29 od 276

(o'piler +rror
a.cs(8-..)/ error (%!017/ @ncorrectl& structured arra& initializer

Whenever we initialize an array, the size of the array stated in the new and the values to be
initialized must be the same in number. In the above case, we are creating an array of three
ints and supplying values to initialize only two of them. We thought, wrongly so, that the
last member a[2] would be initialized to zero. The compiler proved us wrong by giving us an
error instead.

a.cs
class zzz
{
public static void Main()
{
int [] a new int[,];
%&ste'.(onsole.)rite#ine(a[!] $ 9.9 $ a[0]);
}
}

*utput
!.!

An array type is always initialized to the default value of its type. In the above case the
default value is zero as the type of the array is an int.

a.cs
class zzz {
public static void Main()
{
int i !;
int [] a new int[,]{i$$-i$$-i$$};
%&ste'.(onsole.)rite#ine(a[!] $ 9.9 $ a[0] $ 9.9 $ a[.]);
}
}
*utput
!.0..

No astonishing facts for a function! Same rules apply here too. No side efects at all. Each
initialization is done independent of each other and starting from left to right.

a.cs
class zzz
{
public static void Main()
{
&&& a0 new &&&(0);
&&& b0 new &&&(0!);
&&& c0 new &&&(0!!);
int [] a new int[,] ;
tr&
{
a new int[,]{a0-b0-c0};
}
catch ( %&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(a[!] $ 9.9 $ a[0] $ 9.9 $ a[.]);
}
%&ste'.(onsole.)rite#ine(a[!] $ 9.9 $ a[0] $ 9.9 $ a[.]);
243884994.doc 33 od 276
}
}
class &&&
{
public int i;
public &&&(int ;)
{
i ;;
}
public static i'plicit operator int ( &&& z)
{
%&ste'.(onsole.)rite#ine(9op 9 $ z.i);
if ( z.i 0!)
throw new %&ste'.+4ception();
return z.i;
}
}
*utput
op 0
op 0!
!.!.!
!.!.!

We create an array of ints and initialize them to objects that smell like yyy's. There is no
problem in doing so as the operator int converts the yyy object into an int.

For the object b1 whose instance variable i has a value 10, we throw an exception. Due to
this, the compiler at run time undoes the initialization of array member a[0] to 1 and it falls
back to the earlier default value of zero. This is the frst task it performs on exceptions. The
array members have to be initialized as the initialization statement is in a try block. If we fail
to do so, we get an 'uninitialized variable error'. In this way, we fool the compiler into doing
our bidding.

The array members are, by default, initialized to zero before they get their new values. The
array access element must not be less than zero or greater than the size of the array, or else
an exception is thrown. An initialization statement of int [] a = new int[10][] creates an array
of ten members and each of them in turn is considered to be an array. Each of the 10 arrays
being reference types is initialized to a value null. We cannot, however, do the following.

a.cs
class zzz
{
public static void Main()
{
int [][] a new int[,][.];
}
}

(o'piler +rror
a.cs(8-.8)/ error (%!017/ @ncorrectl& structured arra& initializer
a.cs(8-.5)/ error (%0!!./ ; e4pected
a.cs(8-.5)/ error (%08.8/ @nvalid e4pression ter' 3]3
Ha Ha Ha!!! We are overjoyed as we confused the compiler to no end. One error on our part
and the compiler bought the entire cavalry out by giving us three errors instead. We are
forbidden to initialize a sub array at the time of creation, hence the error. They are to be
initialized later and individually, as follows.

a.cs
243884994.doc 31 od 276
class zzz
{
public static void Main()
{
int [][] a new int[,][];
a[!] new int[0];
a[0] new int[0!];
%&ste'.(onsole.)rite#ine(a.#ength $ 9 9 $ a[!].#ength $ 9 9 $ a[0].#ength );
}
}

*utput
, 0 0!

The size of the array a is 3 and it contains three members which are an array of ints. The
frst one, a[0] contains only one int and the second a[1] has 10 ints. Not permitting the
initialization of sub arrays at the time of creation brings in fexibility as we can now initialize
each of them to any size we wish rather having the same size. In life also, one size does not
ft all ever.

A rectangular array is an array of arrays where all the sub arrays have the same length.
This is more efcient than a multi dimensional array. If we write new int[10][] and initialize
the sub arrays to new int[5], we are creating one main array and 10 sub arrays. However, if
we write int [,] a = new int[10,5] we are only creating one array in all and also it uses only
one statement. Compare and contrast it with the above and choose the one you are most
comfortable with. It is fnally a personal decision.



Parameter arrays

When we use a params parameter in its normal form, the data type to be specifed must be
of a type that can be directly or implicitly converted to the parameter array type. A similar
thing happens with a value parameter. In the expanded form, we do not use an array but
pass the parameters individually, which then is converted into an actual array. It is the
compiler's job to create an array of the same data type and width and then initialize the
members with the values stated as parameters. Finally, it uses this array as the params
parameter to the function.

Thus, if we had a simple function abc (params object [] a) and we called the function abc as
abc(1,2,"hell"), then an array would be created as new object[] { 1,2,"hell"}. If the function is
called with no parameters, then the array becomes an empty array. An array is created after
all.

a.cs
class zzz
{
public static void Main()
{
int [] a new int[!];
}
}

There is nothing to feel sorry about when we create an empty array.

243884994.doc 32 od 276
a.cs
class zzz
{
public static void Main()
{
zzz a new zzz();
int i 0!;
a.abc(i);
%&ste'.(onsole.)rite#ine(i);
}
public void abc(ob;ect a)
{
%&ste'.(onsole.)rite#ine(a);
a 0!!;
}
}

*utput
0!
0!

When calling the function abc, if we do not specify ref or out, then there is no way in hell
that the value of i can be changed within the function. In the above case, we have upgraded
our value parameter, i, to an object using a method called boxing. In spite of it being a
reference type, a, which is an instance of class object, will not refect any changes made
within the function abc in Main. If you do not believe us, cast i to an object explicitly and yet
nothing changes.

The ther dds and Ends

a.cs
class zzz
{
public static void Main()
{
int [] a new int[0!];
a[0-.] 0!;
}
}

(o'piler +rror
a.cs(5-0)/ error (%!!../ )rong nu'ber of indices inside []- e4pected 303

A one- or a single-dimensional array can only have one indice and not 2. The error tells us
that the compiler checks whether we have given the right number of indices but it does not
check whether we have exceeded the bounds of the array. All this is part and parcel of the
array error handling enforced by the compiler. It is unfortunately very selective in the type of
error handling and some common errors go undetected.
a.cs
struct zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(9hi9);
}
}

*utput
hi
243884994.doc 33 od 276

There is no rule in the world that mandates the Main function to be placed only in a class.
Here we placed it in a structure. Main is the last function in the world to complain.

You cannot place functions anywhere else in your program. All the rules binding other
functions apply to Main also.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(9hi in zzz9);
}
}
class &&& / zzz
{
public new static void Main()
{
%&ste'.(onsole.)rite#ine(9hi in &&&9);
}
}

(o'piler +rror
a.cs(,-.!)/ error (%!!01/ 2rogra' 3a.e4e3 has 'ore than one entr& point defined/ 3zzz.Main()3
a.cs(0!-.>)/ error (%!!01/ 2rogra' 3a.e4e3 has 'ore than one entr& point defined/ 3&&&.Main()3


In the above program, we have two functions called Main. This results in an error. Hey, but
wait a minute, they are in separate classes, so shouldn't it be acceptable? The compiler
however does not think so. It does not like this specifc function, Main being repeated twice
in separate classes. We cannot use the modifers virtual, new or override, as the function is
static. Always follow the simple dictum, only one love, so only one function called Main!

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
a.aa 0!;
a.abc(ref a.aa);
}
}
class &&&
{
public int aa
{
get {return !;}
set {}
}
public void abc( ref int b)
{
}
}

(o'piler +rror
a.cs(1-00)/ error (%!.!5/ : propert& or inde4er 'a& not be passed as an out or ref para'eter

243884994.doc 34 od 276
If it walks like a duck, quacks like a duck, it's a duck. A property looks and feels like a
variable but it is actually a series of functions or to be technically correct, accessors. Only a
storage or memory location can be passed as a ref or an out parameter. A property is not
allocated any memory and thus cannot be used wherever a variable can be used. All the
restrictions of static apply to static properties too. The get accessor must terminate either in
a return or a throw statement. Control cannot fow of the accessor body at the end. A set
accessor is like a function that returns void and it has an implicit parameter value.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
a.aa 0!;
%&ste'.(onsole.)rite#ine(a.aa);
}
}
class &&&
{
public int ;-A !;
public int aa
{
get
{
%&ste'.(onsole.)rite#ine(9get 9 $ ;);
if ( A !)
{
aa aa $ 0; BB aa$$
A 0;
}
return ;;
}
set
{
; value;
%&ste'.(onsole.)rite#ine(9set 9 $ ;);
}
}
}

The above program does not give any compiler error. We are allowed to change the property
value, within a property. If you do so, the program goes into an indefnite loop. This is
regardless of the fact that by using the value of variable we ensure that the statement aa++
is executed only once.
a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
a.aa 0!;
%&ste'.(onsole.)rite#ine(a.aa);
}
}
class &&&
{
public int ;-A !;
public int aa
{
243884994.doc 32 od 276
get
{
%&ste'.(onsole.)rite#ine(9get0 9 $ ;);
aa >;
%&ste'.(onsole.)rite#ine(9get. 9 $ ;);
return ;;
}
set
{
; value;
%&ste'.(onsole.)rite#ine(9set 9 $ ; );
}
}
}

*utput
set 0!
get0 0!
set >
get. >
>

We all have been dumb many a times in life. One proof lies in the above program. Here in
the get accessor of property aa, we are calling the set accessor by giving the statement aa =
4. No indefnite loop but crazy answers!! The long and short story for that matter is, do not
call the get or set accessor with a property directly.
An indexer is similar to a property other than the fact that the indexers, accessors take or
accept parameters. A property is identifed by a name, an indexer by its signature.

a.cs
class zzz
{
public static void Main()
{ }
}
class &&&
{
public static int this[int i]
{
get {return !;}
set {}
}
}

(o'piler +rror
a.cs(7-06)/ error (%!0!5/ =he 'odifier 3static3 is not valid for this ite'

Unlike a property, an indexer cannot be static. A property cannot be accessed like an array
or by element access, similar to an indexer. A get and a set accessor share the same
parameter list that the indexer has. A property has no such luck and no parameters at all
but the set receives the implicit parameter value.

In an interface, the only job of an indexer is to declare the signature. Interfaces are smart.
They cannot carry code. All that they do is boss around. They decide the signatures of
functions, indexers etc. so that other people who implement them, follow the rules built in
them.

a.cs
243884994.doc 36 od 276
na'espace aaa
{
int i;
}

(o'piler +rror
a.cs(,-0)/ error (%!005/ : na'espace does not directl& contain 'e'bers such as fields or
'ethods

The compiler is very particular about what you place where. We are not allowed to place
methods and felds directly in a namespace or outside a namespace. Nothing stops us from
placing them in a class. These are the regulations to be followed in the compiler world.

a.cs
na'espace aaa
{
public class zzz
{
public static void Main()
{
aaa a;
}
}
}

(o'piler +rrors
a.cs(1-0)/ error (%!007/ 3aaa3 denotes a 3na'espace3 where a 3class3 was e4pected

Wouldn't you be surprised if you see a tiger instead of a dog? Similarly, we surprised the
compiler. It expected a class/type name and instead comes by the name of a namespace.
The above error is a generic error. If we give the name of a property instead of a class, the
error number will be the same, but the message would change. A single error to take care of
multiple possibilities. The generic rule clearly states that a CS0118 error number is emitted
whenever the compiler gets something that it does not expect.

a.cs
na'espace aaa
{
na'espace bbb.ccc
{
}
}
(o'piler +rror
a.cs(,-00)/ error (%!0,>/ (annot use <ualified na'espace na'es in nested na'espace
declarations

We can have namespaces within namespace with nary a nod from the compiler. What we
cannot have is a qualifed namespace within another namespace. Why? Ask the designers of
the language.

a.cs
class zzz
{
}
using %&ste';

(o'piler +rror
a.cs(>-0)/ error (%08.6/ : using clause 'ust precede all other na'espace ele'ents
243884994.doc 37 od 276

We all wanted to come in class but using always beat us to the draw. We have no choice but
to start our code with the using clause. This is a rule imposed upon us by the top brass and
obey, we must.

a.cs
new class zzz { }

(o'piler +rrors
a.cs(0-0)/ error (%08,!/ Ke&word new not allowed on na'espace ele'ents
You cannot use new on namespace elements like class or struct. This is because we create
them in a global namespace.

a.cs
using aa %&ste';
using aa %&ste';

(o'piler +rror
a.cs(.-1)/ error (%08,1/ =he using alias 3aa3 appeared previousl& in this na'espace

The compiler hates seeing two of anything even though they are the same and cause no
trouble. Thus, two using's are out whether they are the same or diferent. We are not
allowed to even change our mind.

a.cs
class zzz
{
public static void Main()
{
string s LMh
iM;
%&ste'.(onsole.)rite#ine(s);
}
}

*utput
h
i

The @ sign lets you start a string with ". The single change is that you can press enter and
move to the next line, ending the string there. The compiler doesnt show any errors.

a.cs
class zzz
{
int i {0};
}

(o'piler +rror
a.cs(,-6)/ error (%!5../ (an onl& use arra& initializer e4pressions to assign to arra& t&pes. =r&
using a new e4pression instead.

The array notation can only be used with the array data type. The {} brackets are to be used
exclusively with arrays only.

a.cs
class zzz
{
243884994.doc 38 od 276
public int[] a { .- ,- {>}};
}
(o'piler +rror
a.cs(,-.5)/ error (%!5.,/ :rra& initializers can onl& be used in a variable or field initializer. =r&
using a new e4pression instead.

We cannot use the {} brackets to initialize an array within an array. For that we have to use
the keyword new instead. The {} can only be used for a single dimensional array.

a.cs
public class zzz
{
int a[.];
}

(o'piler +rror
a.cs(,-5)/ error (%!58!/ %&nta4 error- bad arra& declarator. =o declare a 'anaged arra& the ranA
specifier precedes the variable3s identifier

There is lots that the C# language has copied from C and C++ and along the way, it has
changed the syntax of a few in the language. The array brackets must come before the name
of the variable and we are not allowed to state the size of the array within them either. All
this worked very well in C/C++, C# in this case looks a lot like Java.

a.cs
class zzz
{
public static void Main(string a [] )
{
}
}

(o'piler +rror
a.cs(,-,>)/ error (%088./ :rra& t&pe specifier- []- 'ust appear before para'eter na'e

Array brackets [] can only be in one place. This is before the name of the array, not after.
God alone knows why?!

a.cs
class zzz
{
public static void Main()
{
int[] a {0-.-,};
foreach (int in a)
{
}
}
}

(o'piler +rror
a.cs(5-0>)/ error (%!.,!/ =&pe and identifier are both re<uired in a foreach state'ent

)he foreach has a certain synta$ and it ets you iterate over an o'4ect. )hus it re%uires the
name of a varia'e that #i contain the individua vaues of the coection o'4ect. 5e faied to
"ive the int varia'e a name and hence "ot sadded #ith an error.
Chapter 2
243884994.doc 39 od 276

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 ; >!;
}
}

(o'piler +rrors
a.cs(1-0)/ error (%0!.,/ +'bedded state'ent cannot be a declaration or labeled state'ent

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# fags 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 fag a warning and not an error. To fgure out
whether a statement is reachable or not, C# does fow analysis.

a.cs
public class zzz
{
public static void Main()
{
int i ,!;
const int ; ,!;
if ( i ,)
%&ste'.(onsole.)rite#ine(9hi9);
if ( ; ,)
%&ste'.(onsole.)rite#ine(9hi9);
}
}

(o'piler )arning
a.cs(0!-0)/ warning (%!05./ Hnreachable 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.

243884994.doc 43 od 276
In the case of the frst 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 fow
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 (4) if (&) I(); else N();

if (4) {
if (&) {
I();
}
else {
N();
}
}

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(0);
a.abc(0!);
}
void abc(int i)
{
switch (i)
{
case !/
%&ste'.(onsole.)rite#ine(9zero9);
breaA;
case 0/
%&ste'.(onsole.)rite#ine(9one9);
breaA;
default/
%&ste'.(onsole.)rite#ine(9end9);
breaA;
}
}
}

*utput
one
end

243884994.doc 41 od 276
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 !/
%&ste'.(onsole.)rite#ine(9zero9);
case 0/
%&ste'.(onsole.)rite#ine(9one9);
breaA;
}
}
}

(o'piler +rror
a.cs(7-0)/ error (%!05,/ (ontrol cannot fall through fro' one case label (3case !/3) to another

The case statement has its own set of complex rules. The frst 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(!);
}
void abc(int i)
{
switch (i)
{
case !/
%&ste'.(onsole.)rite#ine(9second9);
goto case 0;
case 0/
%&ste'.(onsole.)rite#ine(9first9);
breaA;
}
243884994.doc 42 od 276
}
}



*utput
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(!);
}
void abc(int i)
{
switch (i)
{
case !/
%&ste'.(onsole.)rite#ine(9second9);
goto case 0;
%&ste'.(onsole.)rite#ine(9hi9);
case 0/
%&ste'.(onsole.)rite#ine(9first9);
breaA;
}
}
}

(o'piler )arning
a.cs(08-0)/ warning (%!05./ Hnreachable code detected

*utput
second
first

Here we have confused the compiler to no end and we advise you not to write such code. At
frst, 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();
243884994.doc 43 od 276
z.abc(!);
z.abc(,);
z.abc(0);
}
void abc(int i)
{
switch (i)
{
case !/
%&ste'.(onsole.)rite#ine(9zero9);
goto case ,;
case 0/
%&ste'.(onsole.)rite#ine(9one9);
goto default;
case ,/
%&ste'.(onsole.)rite#ine(9two9);
breaA;
default/
%&ste'.(onsole.)rite#ine(9last9);
breaA;
}
}
}

*utput
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 !/
while ( true) ;
case 0/
return ;
case ,/
throw new %&ste'.+4ception();
}
}
}

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.
243884994.doc 44 od 276

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 !/
%&ste'.(onsole.)rite#ine(9zero9);
breaA;
case !/
%&ste'.(onsole.)rite#ine(9one9);
breaA;
}
}
}

(o'piler +rror
a.cs(0,-5)/ error (%!08./ =he label 3case !/3 alread& occurs in this switch state'ent

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 frst, the second, or both, or, even better, none.
Decisions, decisions, they are so difcult to make, so the C# compiler drops by an error.

a.cs
public class zzz
{
int ; 0!;
public static void Main()
{
}
void abc(int i)
{
switch (i)
{
case ;/
breaA;
case ; > >/
%&ste'.(onsole.)rite#ine(9zero9);
breaA;
}
}
}
243884994.doc 42 od 276

(o'piler +rror
a.cs(00-5)/ error (%!08!/ : constant value is e4pected
a.cs(0,-5)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3bool3 to 3int3

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
predefned values only. This limits our fexibility 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(1);
z.abc(1!);
}
void abc(int i)
{
switch (i > 7)
{
case > > 8/
%&ste'.(onsole.)rite#ine(9first9);
breaA;
case > " 8/
%&ste'.(onsole.)rite#ine(9second9);
breaA;
}
}
}

*utput
first
second

We could not for the life of us fgure 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 >! > 8/
%&ste'.(onsole.)rite#ine(9third9);
breaA;

and we got the following error

(o'piler +rror
a.cs(06-5)/ error (%!08./ =he label 3case 0/3 alread& occurs in this switch state'ent

z.abc(7) initializes i to 7, hence the switch condition becomes false. The case may show
logical operations but fnally has either 1 or 0 standing for true or false. As the switch
results in false, the frst 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
243884994.doc 46 od 276
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();
&&& a new &&&();
z.abc(a);
}
void abc(&&& i)
{
switch (i)
{
case !/
%&ste'.(onsole.)rite#ine(9second9);
breaA;
case 0/
%&ste'.(onsole.)rite#ine(9first9);
breaA;
}
}
}
class &&&
{
}

(o'piler +rror
a.cs(00-6)/ error (%!080/ : value of an integral t&pe e4pected

The C# compiler informs us that a switch statement can only take certain integral types. To
be more specifc, 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();
&&& a new &&&();
z.abc(a);
}
void abc(&&& i)
{
switch (i)
{
case !/
%&ste'.(onsole.)rite#ine(9second9);
breaA;
case 0/
%&ste'.(onsole.)rite#ine(9first9);
breaA;
}
}
}
class &&&
{
243884994.doc 47 od 276
public static i'plicit operator int ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator9);
return 0;
} }

*utput
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();
&&& a new &&&();
z.abc(a);
}
void abc(&&& i)
{
switch (i)
{
case !/
%&ste'.(onsole.)rite#ine(9second9);
breaA;
case 0/
%&ste'.(onsole.)rite#ine(9first9);
breaA;
}
}
}
class &&&
{
public static i'plicit operator int ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator9);
return 0;
}
public static i'plicit operator b&te ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator9);
return 0;
}
}

(o'piler +rror
a.cs(00-6)/ error (%!080/ : value of an integral t&pe e4pected

We really got of 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 fags an error. The switch expression decides on the
governing type of the statement.
243884994.doc 48 od 276
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(.);
z.abc(,);
}
void abc(int i)
{
switch (i)
{
case !/
%&ste'.(onsole.)rite#ine(9first9);
breaA;
case ,/
case ./
%&ste'.(onsole.)rite#ine(9second9);
breaA;
}
}
}

*utput
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 fnal answer so changing the order doesn't change anything.
For that matter, the default came frst 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(!);
}
void abc(int i)
{
switch (i)
{
case !/
int ; 1;
243884994.doc 49 od 276
%&ste'.(onsole.)rite#ine(;);
breaA;
}
}
}

*utput
1

a.cs
public class zzz
{
public static void Main()
{
zzz a new zzz();
a.abc(!);
}
void abc(int i)
{
switch (i)
{
int ; 1;
case !/
int A 1;
breaA;
case 0/
%&ste'.(onsole.)rite#ine(A);
breaA;
}
}
}

(o'piler +rror
a.cs(0.-0)/ error (%08.,/ =he Ae&word case or default 'ust precede code in switch blocA

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.

(o'piler +rror
a.cs(05-.5)/ error (%!058/ Hse of unassigned local variable 3A3

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;
243884994.doc 23 od 276
aa/
}
}

(o'piler +rror
a.cs(0!-0)/ error (%08.8/ @nvalid e4pression ter' 3}3
a.cs(00-0)/ error (%0!!./ ; e4pected

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. ;)

!oops

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 0;
do
{
%&ste'.(onsole.)rite(i $ 9 9);
i$$;
} while ( i " ,);
}
}

*utput
0 . ,

Earlier we learnt about the diferences between the for and the while and the scientifc
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 diference 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
243884994.doc 21 od 276
public class zzz {
public static void Main() {
int i 0;
do
{
%&ste'.(onsole.)rite(i $ 9 9);
i$$;
} while ( i " !);
i 0;
while ( i " ! ) {
%&ste'.(onsole.)rite(i $ 9...9);
i$$;
}
}
}

*utput
0
In the do statement, the loop condition is checked at the end of the frst 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 0; i " 0! ; i$$)
%&ste'.(onsole.)rite(i $ 9...9);
%&ste'.(onsole.)rite(i);
}
}

(o'piler +rror
a.cs(1-..)/ error (%!0!,/ =he na'e 3i3 does not e4ist in the class or na'espace 3zzz3

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 0; ; i$$)
%&ste'.(onsole.)rite(i $ 9...9);
}
}

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 fag 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.

243884994.doc 22 od 276
a.cs
public class zzz
{
public static void Main()
{
int [] a new int[,]{0-.-,};
foreach ( int i in a)
{
%&ste'.(onsole.)rite(i $ 9 9);
i 0!;
zzz.abc(ref i);
%&ste'.(onsole.)rite#ine(i);
}
}
public static void abc(ref int ;)
{
%&ste'.(onsole.)rite(9abc 9 $ ; $ 9 9);
; 0!!;
}
}

(o'piler +rror
a.cs(6-0)/ error (%05!>/ (annot assign to 3i3 because it is readOonl&
a.cs(0!-0,)/ error (%05!8/ (annot pass 3i3 as a ref or out argu'ent because it is readOonl&

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.

"ump 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 fve
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 beftted in.

a.cs
public class zzz
{
public static void Main()
{
breaA;
}
}

(o'piler +rror
a.cs(8-0)/ error (%!0,6/ Fo enclosing loop out of which to breaA 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 0- ; 0;
243884994.doc 23 od 276
for ( i 0; i" 0! ; i$$) {
if ( i ,)
breaA;
for ( ; 0; ; " 0!; ;$$)
{
%&ste'.(onsole.)rite#ine(i $ 9.9 $ ;);
if ( ; 0)
breaA;
}
}
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
}
}
*utput
0.0
..0
, 0

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 fnicky 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 0- ; 0;
for ( i 0; i" 0! ; i$$)
{
if ( i ,)
breaA;
for ( ; 0; ; " 0!; ;$$)
{
%&ste'.(onsole.)rite#ine(i $ 9.9 $ ;);
if ( ; 0)
goto aa ;
}
}
aa/
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
}
}

*utput
0.0
0 0
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 fnds out that you have
been reading all about goto.
243884994.doc 24 od 276

a.cs
public class zzz {
public static void Main() {
int i 0;
for ( i 0; i" 0! ; i$$)
{
tr&
{
%&ste'.(onsole.)rite#ine(90 tr&9);
tr&
{
%&ste'.(onsole.)rite#ine(9. tr&9);
breaA;
}
finall&
{
%&ste'.(onsole.)rite#ine(9. finall&9);
}
}
finall&
{
%&ste'.(onsole.)rite#ine(90 finall&9);
}
}
%&ste'.(onsole.)rite#ine(i);
}
}

*utput
0 tr&
. tr&
. finall&
0 finall&
0

The for loop starts and enters the frst 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 fnally clause. Hence before it exits the try, it
executes the code in the fnally. Now it remembers that there is one more try statement in
the outstanding. This try also has a fnally clause. Once the code in fnally gets executed, C#
exits from the for loop.

Thus, if the break is nested among 20 fnally clauses, all of them will frst 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 0;
for ( i 0; i" 0! ; i$$)
{
goto aa;
}
{
aa/
%&ste'.(onsole.)rite#ine(i);
243884994.doc 22 od 276
}
}
}

(o'piler +rror
a.cs(7-0)/ error (%!086/ Fo such label 3aa3 within the scope of the goto state'ent

(o'piler )arning
a.cs(00-0)/ warning (%!05>/ =his 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/;
}
}
}

(o'piler +rror
a.cs(8-0)/ error (%!086/ Fo such label 3aa3 within the scope of the goto state'ent

(o'piler )arning
a.cs(1-0)/ warning (%!05./ Hnreachable code detected
a.cs(1-0)/ warning (%!05>/ =his 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 0; i" 0! ; i$$)
{
tr&
{
}
finall&
{
breaA;
return ;
}
}
}
}

(o'piler +rror
a.cs(0.-0)/ error (%!081/ (ontrol cannot leave the bod& of a finall& clause

243884994.doc 26 od 276
(o'piler )arning
a.cs(0,-0)/ warning (%!05./ Hnreachable code detected

(o'piler +rror
a.cs(0,-0)/ error (%!081/ (ontrol cannot leave the bod& of a finall& clause

You cannot quit out of a fnally block with a break or return statement. If you have a short-
lived memory, we will repeat and tell you that the fnally clause of a try is always called. You
cannot leave a fnally abruptly. All code in fnally 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 0; i" ,; i$$)
{
%&ste'.(onsole.)rite#ine(9(9 $ i);
continue;
%&ste'.(onsole.)rite#ine(9:9 $ i);
}
}
}
*utput
(0
(.
(,

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 fnally with continue in place of break.

Similar to break, you cannot place continue in a fnally block. A break says bye-bye to a loop
whereas a continue restarts from the frst 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 0!;
goto aa ;
243884994.doc 27 od 276
aa/ ;
}
}

C# stores the name of a label and the name of a variable in diferent sections of the
program. As they belong to diferent namespaces, the above program does not generate any
errors. Had C# stored the name of a function and name of a variable in a diferent
namespaces, we could use the same name for both of them too.

A good programming language design looks at diferent namespaces it requires and the
contents within them. The teeming millions like us then abide by their decision and
pontifcate 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 ther dds and Ends

a.cs
class zzz
{
public static void Main()
{
aa/ ;
aa/ ;
}
}

(o'piler +rrors
a.cs(1-0)/ error (%!0>!/ =he label 3aa3 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 8 ;
}
}


(o'piler +rrors
a.cs(8-0)/ error (%!08,/ : goto case is onl& valid inside a switch state'ent

A goto case statement only fts in a case statement. It cannot be used outside a case.

a.cs
class &&&
{
}
class zzz
{
public static void Main()
{
la/
243884994.doc 28 od 276
;
{
la/
;
goto la;
}
}
}

(o'piler +rror
a.cs(0,-0)/ error (%!087/ =he label 3la3 shadows another label b& the sa'e na'e 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) ;
}
}
(o'piler )arning
a.cs(8-0.)/ warning (%!5>./ 2ossible 'istaAen null state'ent

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 5;
switch(i)
{
}
}
}

(o'piler )arning
a.cs(7-0)/ warning (%08../ +'pt& switch blocA

6m!ty vesses make the most noise. )he com!ier does not understand anythin" em!ty
incudin" a s#itch statement. 7ea$+ it is ony a #arnin" and can 'e i"nored8
Chapter 3

Enums

An enum is at the same level as a class, struct and interface. It performs a similar job of
creating a new data type.

243884994.doc 29 od 276
a.cs
class zzz
{
public static void Main()
{
}
}
enu' &&&
{
a0-blacA-hell
}

We have created a new datatype yyy using enum. It has three values, namely, a1, black and
hell. The words we write within an enum can be anything, it is left entirely on us.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine((int)&&&.blacA);
}
}
enu' &&&
{
a0-blacA-hell
}

*utput
0

An enum can be considered as a static object, hence black can be referred to as yyy.black.
In the WriteLine method, when we cast yyy.black to int, the output returned is an integer
value of 1. Without any cast, the output is displayed as black.

In a sense, it behaves like an array where a1 has a value of zero, black has a value of 1 and
hell 2. Wherever we refer to yyy.black, it is like writing the number 1. At one level an enum
is a word that stands for a constant number. An enum type is called a distinct type with
named constants.

a.cs
class zzz {
public static void Main()
{
%&ste'.(onsole.)rite#ine((b&te)&&&.blacA);
}
}
enu' &&& / b&te
{
a0-blacA-hell
}


*utput
0

In the earlier example on enum, the data type for the members was int as we did not specify
the underlying data type. Here we are specifying byte as the underlying data type.
243884994.doc 63 od 276
The valid underlying types are byte, sbyte, short, ushort, int, uint, long or ulong
respectively. A char cannot be the underlying type for an enum data object as a char stores
Unicode characters and not numbers.

a.cs
class zzz
{
public static void Main()
{
}
}
enu' &&&
{
a0-blacA-hell
}
enu' 444 / &&&
{
}

(o'piler +rror
a.cs(0.-0)/ error (%0!!7/ =&pe b&te- sb&te- short- ushort- int- uint- long- or ulong e4pected

Enums cannot derive from other enums and can only have the above mentioned types as
the underlying data types.

a.cs
class zzz
{
public static void Main()
{
}
}
enu' &&&
{
a0-blacA-hell
}
class 444 / &&&
{
}

(o'piler +rror
a.cs(00-1)/ error (%!8!6/ 34443 / cannot inherit fro' sealed class 3&&&3

Internally, an enum is treated as a sealed class and thus follows all the rules pertaining to
sealed classes.

a.cs
class zzz
{
}
enu' &&&
{
a0-blacA-hell-a0
}

(o'piler +rror
a.cs(5-08)/ error (%!0!./ =he class 3&&&3 alread& contains a definition for 3a03

243884994.doc 61 od 276
An enum cannot have two members with the same name. Every enum member has a value
which if not specifed starts with 0 and the next enum member increments it by 1.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine((int)&&&.a0 $ 9 9 $ (int)&&&.blacA $ 9 9 $ (int)&&&.hell $ 9 9 $ (int)&&&.a.);
}
}
enu' &&&
{
a0 >-blacA-hell 6-a.
}

*utput
> 8 6 0!

We can, however, specify the constant values for the enum members. a1 starts at 4 and as
we have not specifed a value for black, it is 5. Hell, though, starts at 9 and a2, the next
member, increases it by one thus getting a value of 10.

a.cs
class zzz
{
public static void Main()
{
}
}
enu' &&& / b&te
{
a0 >!!-blacA-hell 6-a.
}

(o'piler +rror
a.cs(6-5)/ error (%!!,0/ (onstant value 3>!!3 cannot be converted to a 3b&te3

If we try and go beyond the range of values supported by the underlying data type, we
receive an error similar to the one we'd get if we initialize a byte to a value beyond its
capacity.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine((int)&&&.a0 $ 9 9 $ (int)&&&.blacA $ 9 9 $ (int)&&&.hell $ 9 9 $ (int)&&&.a.);
}
}
enu' &&&
{
a0 >-blacA-hell 6-a.a0
}

*utput
> 8 6 >
Nothing stops us from initializing more than one enum member to the same constant value.

243884994.doc 62 od 276
a.cs
class zzz
{
public static void Main()
{
&&&.a0 0!;
}
}
enu' &&&
{
a0 >-blacA-hell 6-a.a0
}

(o'piler +rror
a.cs(8-0)/ error (%!0,0/ =he leftOhand side of an assign'ent 'ust be a variable- propert& or
inde4er
a.cs(8-0!)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3int3 to 3&&&3

An enum member behaves like a constant whose value cannot be changed.

a.cs
using %&ste';
enu' &&&
{
red-green-blue ,
}
class zzz
{
public static void Main() {
(onsole.)rite#ine(444(&&&.blue));
(onsole.)rite#ine(444(&&&.green));
}
static string 444(&&& c) {
switch (c) {
case &&&.red/
return 9red9;
case &&&.blue/
return 9blue9;
case &&&.green/
return 9green9;
default/
return 9invalid9;
}
}
}

*utput
blue
green

Here we have created an enum that has three members. The switch statement can take any
data type and the case statement uses the member names rather than numbers to compare
for a match. This makes our program more readable and easier to document.

a.cs
enu' &&& {
red green -green
}
class zzz
{
public static void Main()
243884994.doc 63 od 276
{
}
}

(o'piler +rror
a.cs(,-0)/ error (%!00!/ =he evaluation of the constant value for 3&&&.red3 involves a circular
definition

Similar to constants, we cannot have a circular defnition. Here red depends on green and
the value of green is one more than the value of red. This circular defnition causes an error.

An enum refers to its own members without a qualifer whereas any external identifer must
precede it with the name of the enum. By default, an enum and its members are public. We
can explicitly use the public modifer if we so choose to. The same accessibility rules of
classes apply here also.
a.cs
class zzz
{
public static void Main()
{
}
}
enu' &&& / %&ste'.+nu'
{
red -green
}

(o'piler +rror
a.cs(7-0)/ error (%0!!7/ =&pe b&te- sb&te- short- ushort- int- uint- long- or ulong e4pected

Every enum data type is automatically derived from System.Enum. However, we cannot
derive from it explicitly. The enum class has also been derived from three interfaces,
IComparable, IFormattable and IConvertible.

a.cs
class zzz {
public static void Main()
{
%&ste'.(onsole.)rite#ine(&&&.green.(o'pare=o(&&&.blue));
%&ste'.(onsole.)rite#ine(&&&.green.(o'pare=o(&&&.red));
%&ste'.(onsole.)rite#ine(&&&.green.(o'pare=o(&&&.green));
}
}
enu' &&&
{
red-green-blue
}

*utput
O0
0
!

Many a times we would like to compare between enums as we do with strings. We may want
to know whether an enum member holds a value larger, equal to or smaller than another
enum member. As all enums fnally derive from the Enum class, they have a function called
CompareTo. This function is not static so we have to execute it through a member. The
243884994.doc 64 od 276
member red has a value 0, green 1 and blue 2. Thus yyy.green is smaller than blue but
larger than red. A negative answer is smaller and a positive value other than zero is larger.

a.cs
class zzz {
public static void Main()
{
%&ste'.(onsole.)rite#ine(&&&.Ior'at(t&peof(&&&)-&&&.green-9P9));
%&ste'.(onsole.)rite#ine(&&&.Ior'at(t&peof(&&&)-&&&.green-9d9));
}
}
enu' &&&
{
red-green-blue
}

*utput
!!!!!!!0
0

Format is a freely available static function with every enumeration. Hence we can access it
through the yyy class. The frst parameter is the data type of enum, the second the value of
the enum member and fnally a string specifying the format i.e. hex or decimal.

a.cs
class zzz
{
public static void Main()
{
string [] a;
a &&&.NetFa'es(t&peof(&&&));
foreach ( string s in a)
%&ste'.(onsole.)rite#ine(s);
}
}
enu' &&&
{
red-green-blue
}

*utput
red
green
blue

The static function GetNames accepts an object of type Type and it returns an array of
strings. In our case, the array has the length of 3 as yyy has 3 members. The foreach
statement is used to displays them one by one.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(&&&.green.=o%tring());
}
}
enu' &&&
{
red-green-blue
243884994.doc 62 od 276
}

*utput
green

We can use a large number of pre-defned conversion functions with enums to convert from
one data type to another.

a.cs
public enu' &&&
{
red -green
}
class zzz
{
public static void Main()
{
int i 0;
if ( i &&&.red)
;
}
}

(o'piler +rror
a.cs(0!-5)/ error (%!!06/ *perator 33 cannot be applied to operands of t&pe 3int3 and 3&&&3

An enum is a distinct data type and it cannot be equated to an int. We need to overload the
operator to perform comparisions between them.

a.cs
public enu' &&&
{
red -green
}
class zzz
{
public static void Main()
{
int i 0;
if ( i (int)&&&.green)
%&ste'.(onsole.)rite#ine(9hi9);
}
}


*utput
hi

The only way to remove the earlier error is by casting yyy.green to int.

a.cs
public enu' aa / int
{
a0-a.-a,
}
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(aa.a. $ 0!);
243884994.doc 66 od 276
%&ste'.(onsole.)rite#ine(0! $ aa.a,);
}
}

*utput
00
0.

The + operator is internally overloaded to add a number to an enum. There are actually two
such operators, the order of priority does not matter. These operators are available each
time we create an enum. We now have a choice of using the earlier members of the enum
class or the free operators.

a.cs
public enu' aa / int
{
a0-a.-a,
}
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(aa.a. $ aa.a,);
}
}

(o'piler +rror
a.cs(6-.5)/ error (%!!06/ *perator 3$3 cannot be applied to operands of t&pe 3aa3 and 3aa3

However, we cannot add two enums members even though they belong to the same enum
family. Individually they are know to be numbers and can be added up with another number
separately.


a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(&&&.a0 &&&.a.);
%&ste'.(onsole.)rite#ine(&&&.a0 &&&.a,);
}
}
enu' &&&
{
a0 0-a. >-a,0
}

*utput
Ialse
=rue

The comparison operators ==, != etc. get automatically overloaded to work with the user
defned enums. As mentioned earlier, enums are glorifed numbers.

a.cs
class zzz
{
public static void Main()
243884994.doc 67 od 276
{
%&ste'.(onsole.)rite#ine(&&&.a0 444.a0);
}
}
enu' &&&
{
a0 0-a. >-a,0
}
enu' 444
{
a0 0-a. >-a,0
}

(o'piler +rror
a.cs(8-.5)/ error (%!!06/ *perator 33 cannot be applied to operands of t&pe 3&&&3 and 34443
The above error is a direct proof stating that enums may be glorifed numbers but cannot be
used to compare enums members from diferent families. All the other comparison operators
work in exactly the same way.

C# gives the same weight to the enum line of data types as provided to classes. Thus it goes
to great extents to make life easier for us when working with the enum data types.

The ther dds and Ends

a.cs
enu' &&&
{
}
class zzz
{
public static void Main()
{
&&& i (&&&) O0 ;
}
}

(o'piler +rror
a.cs(7-0!)/ error (%!007/ 3&&&3 denotes a 3class3 where a 3variable3 was e4pected
a.cs(7-6)/ error (%!!18/ =o cast a negative value- &ou 'ust enclose the value in parentheses

To cast a -ve value, it must be placed in parenthesis. This may be because of the fact that
the unary minus is also an operator and may confuse the compiler. In doubt, always use
parenthesis. An apple a day keeps the doctor away and an extra parenthesis has done no
one any harm.

a.cs
enu' eee
{
valueQQ
}

(o'piler +rror
a.cs(,-0)/ error (%!!15/ =he enu'erator na'e 3valueQQ3 is reserved and cannot be used

The compiler has a large number of reserved words. It converts the code to a diferent form
altogether. Only for an enum does it not allow us to use the reserved word value__ as it must
be using the same word internally to keep track of the enum. That is what we think but the
right answer is only with the people who designed the language. The long and short is that
243884994.doc 68 od 276
we are not allowed to use certain words in our code at certain places. It is like the charge of
the light brigade. Ours is not to reason why, ours is but to do and die.

a.cs
enu' eee / b&te
{
a .88-b
}

(o'piler +rror
a.cs(,-6)/ error (%!8>,/ 3eee.b3/ the enu'erator value is too large to fit in its t&pe

A byte can only store values from 0 to 255. In the case of an enum, the value of enum
member a is 255 and that of b is 256. However, a byte cannot store such a large number.
Thus the error is reported at b and not at a. Also if we try and equate a to say 300, we get a
diferent error message. This overfow error is reserved exclusively for enums. Others not
invited.
Chapter 4

Types

Type #eclaration

A declaration creates a name in a declaration space. The only declaration that can have the
same name is a constructor, method, indexer or an operator. These are the only entities that
can be overloaded in a type. We cannot have a feld and a method with the same name in a
declaration space. Each time we create a new class, struct or interface, a new declaration
space is created.

The only entities in a class/struct that can share the same name as the class/struct is a
instance constructor or a static constructor. Base class and derived classes live in separate
declaration space, hence the derived class can hide names created in the base class. An
enum also creates its own declaration space.

A local variable declaration space is created by the {} braces or a block. The textual order of
names created is of no importance at all except in certain situations. The initialization of the
variables is carried out in the order they have been created. Local variables must be defned
prior to using them in the remaining program. The enums declaration order is only
important when constant expression values are not used.

There are fve diferent ways to create our own types. These are class, struct, interface,
enum or a delegate. These type declarations can occur in three places only. The usual
suspects are namespaces, struct and the obvious classes.

The compiler, basically, understands only two types, viz, value types and reference types. It
can seamlessly convert any value type into a reference type or an object. This conversion of
value to and from reference types is given a name, boxing and unboxing.

A value type can be one of the two, either a structure or an enum. The simple data types are
predefned structures further divided into numeric, integer or foating point types. We have
243884994.doc 69 od 276
nine integral types sbyte, byte, short, ushort, int, uint, long, ulong and char and two foating
point types foat and double.

a.cs
class zzz / int
{
}

(o'piler +rror
a.cs(0-1)/ error (%!8!6/ 3zzz3 / cannot inherit fro' sealed class 3int3

We cannot derive from any of the value types as they are explicitly sealed. This deterrence
restricts you from tinkering around with the guts of the compiler unnecessarily.

There is one fundamental diference between a value and a reference type. While copying in
a value type, a variable value is copied by creating a separate copy of the variable in
memory. In a reference type, only a reference to the object is copied and not the actual
contents.

All value types have a constructor that is called at the time of creation of the object. Thus,
when we write int i, this free public parameter-less constructor is called. This is also the
default constructor. For all the simple types, this default constructor initializes the variable
to zero or in the case of foating point types, to be specifc, 0.0. Enums are constants and so
their values are also initialized to zero. In the case of a struct, all value types are made zero
and reference type are initialized to null.

a.cs
class zzz
{
public static void Main()
{
aaa a;
}
}
struct aaa
{
public int i;
public string ;;
}

(o'piler )arning
a.cs(0!-0.)/ warning (%!5>6/ Iield 3aaa.i3 is never assigned to- and will alwa&s have its default
value !
a.cs(00-08)/ warning (%!5>6/ Iield 3aaa.;3 is never assigned to- and will alwa&s have its default
value null

Need we add anything more? The compiler after a very long time agrees with us to no end
and repeats what we told you in the earlier paragraph. If error messages and warnings were
more verbose, would you want such weighty books? We surely would be out of a job.

a.cs
class zzz {
int i;
public static void Main()
{
int ; new int();
zzz a new zzz();
243884994.doc 73 od 276
%&ste'.(onsole.)rite#ine(a.i$ 9 9 $ ;);
}
}

(o'piler )arning
a.cs(,-8)/ warning (%!5>6/ Iield 3zzz.i3 is never assigned to- and will alwa&s have its default value
!
*utput
! !

Whether we write int i or new int(), the efect is the same. An int object/variable is created
and the constructor is called. This initializes the variable to zero. Thus, use of new is
optional for the basic types. We thank the compiler for such small mercies.

a.cs
class zzz
{
public static void Main()
{
int ; new int(0!);
}
}

(o'piler +rror
a.cs(8-6)/ error (%!0>,/ =he t&pe 3int3 has no constructors defined

We peeked into the structure System.Int32 to locate for a constructor that accepts one
parameter. But to our dismay, we never found it. The designers chose not to implement such
a constructor, a decision they made. And, it is at our discretion to criticize or not on their
decision. Their volition is a mystery to all of us. Is anyone out there in outer space listening?

Also, as every structure has a constructor with no parameters, which initializes every
member to its default value, we are obviously not permitted to create our own constructor
with no parameters. This would create a piquant situation where we would end up having
two constructors with no parameters. Whenever the compiler gives us something free, we
should be highly indebted to it and never try and repeat the same code. Duplicates are a
problem wherever you go.

The simple data types are a little diferent from the structure types not only because the
compiler considers them to be reserved words in the language but also they are treated in a
slightly diferent way.


First and foremost, a simple type can be initialized by using a literal 123, e.g. int i = 123.
Literals cannot be used with user-defned struct types. They can be used only with simple
types and the value types. The value of a struct can only be determined at run time as the
constructor initializes the members. Thus, a constant expression can be created with simple
value types. Score two for the simple value types.

At the same level, a structure type cannot be a constant. To solve this problem, the compiler
introduced static readonly felds. They have the same efect as constants. Score + point for a
value feld. Lastly, a value type can be converted into other value types for matching a
parameter in a user defned conversion operator. This is not possible for other user defned
conversion parameters involving other structure types.

243884994.doc 71 od 276
$ntegral Types

Warning. Lots of tiresome information ahead and less of Code.

We have nine integral types in all. They amount of memory they require and the range of
values they can handle are as follows.

The sbyte type represents a signed 8-bit integers with values between -128 and 127.

The byte type represents unsigned 8-bit integers with values between 0 and 255.

The short type represents signed 16-bit integers with values between -32768 and
32767.

The ushort type represents unsigned 16-bit integers with values between 0 and
65535.

The int type represents signed 32-bit integers with values between -2147483648 and
2147483647.

The uint type represents unsigned 32-bit integers with values between 0 and
4294967295.
The long type represents signed 64-bit integers with values between
-9223372036854775808 and 9223372036854775807.

The ulong type represents unsigned 64-bit integers with values between 0 and
18446744073709551615.

The char type represents unsigned 16-bit integers with values between 0 to 65535.

The set of possible values for the char type corresponds to the Unicode character set. All this
is copied straight from the documentation. Fastest paragraph ever written. It took exactly
1.2 seconds to accomplish the copy and paste.

As a re-revision, for unary operators +,- and ~ and the binary operators the operand/s are
frst converted to an int, uint, long or ulong. These datatypes can fully represent the range
of values. The unary operator, however, frst converts the operand only to an int or long. The
relational operators only deals with a bool as the result value.

a.cs
class zzz
{
public static void Main()
{
char ; 3:3;
char i (char)58;
char A 58;
}
}

(o'piler +rror
a.cs(1-0!)/ error (%!!,0/ (onstant value 3583 cannot be converted to a 3char3

243884994.doc 72 od 276
A char constant has to be written as a character literal or a number with a cast. If you do
not, the above error will be your reward.



a.cs
class zzz
{
public static void Main()
{
char ; 3:3;
char l (char)58;
char ' 3R4!!>03;
%&ste'.(onsole.)rite#ine(; $ 9 9 $ l $ 9 9 $ ');
}
}

*utput
: : :

As there are many ways to skin a cat, there are many ways to do the same thing in C#.
Thus, we can also use the hexadecimal notation to initialize a char, provided we are using
literals. Also, a cast is the perfect panacea for all our problems. Any errors resulting due to
conversions can always be removed using a cast.

%loating Point

We have two diferent foating point types in the C# programming language, Float and its
elder, smarter brother double. The computer world got together and came out with a
worldwide standard on storing numbers with decimal places in memory. They have to be
stored diferently from a number. This standard could not go nameless and hence it was
called the IEEE 754 standard. It is freely available to all and stores foats and doubles as 32
bit single precision and 64 bit double precision numbers in memory.

Zero denotes an absence of anything. If something is not present, then how in the frst
place, can we detect it? Do we have a concept of positive zero and negative zero? These are
questions to be answered by philosophers and the mathematics doctors.

Most of the time zeroes are the same but at times, albeit rarely, they are diferent. If we have
the time, we will give you a short exposition on zeroes. To complicate matters even further,
infnity also cannot be measured. The moment you can, it stops being infnite. It is like the
sound of a one hand clap. Thus we have like zeroes, positive and negative infnity. 1.0/0.0 is
+ve infnity and -1.0/0.0 is negative infnity. When we divide a zero by a zero. What should
we get as the answer. Obviously something that is Not a Number or NAN, which is also an
invalid number.

Let us now understand the vastness of numbers that a foating point type can store. If the
formula can be written as s * m * 2^e where s is either 1 or -1. The range of m is from o to 2
^24 for a foat and e lies form -149 to 104. For a double m lies form 0 to 2^53 and e form
-1075 to 970. To put the above numbers in the right perspective, a foat can represent
values ranging from 1.5 + 10"45 to 3.4 + 1038 with a precision of 7 digits whereas the
double data type is nearly double that of the foat. It is from 5.0 + 10"324 to 1.7 + 10308
with a precision of 15-16 digits.

243884994.doc 73 od 276
If you walked in late to the party, for binary operators, the rule is very simple. First, there is
a conversion to double and then to a foat for any one of the operands that is a integer type
and the other, a double or foat. Floating point types hate producing exceptions and thus do
not throw them at all. They show their displeasure in other ways. If the answer is too small
for the destination type, a +ve or a -ve zero is the fnal result and if it is too large, it will be
+ve or -ve infnity. If the answer does not follow the IEE 754 specifcations, a NAN is
outputted. A NaN does not like to coexist with any other number, and thus if any operand
produces a NaN, the result of the operation is a NaN.

C# is very fexible in the handling of foating point types in memory. The above range is only
suggestive at the lower end. The compiler only enforces a bare minimum and not a
maximum. Hardware architectures keep evolving and there are some, where a foating point
number is stored internally using more memory than what is specifed by the IEEE 754
specifcations. If the compiler had to adhere to the IEEE format, it would slow down the
machine even though we are asking the computer to use less memory to do the calculations.
Thus, the compiler allows us even more leeway here. It lets us more than double the range of
the foating point and also does not compromise on speed. More is good, less is bad!
#ecimal Types

If you have been impressed by the foating point types, now is the time to remove the scales
from your eyes. For a large number of fnancial and monetary computations, the foating
point types are not accurate enough and introduce a rounding of error. The decimal type
needs 128 bits or 16 bytes of memory to store itself. The range, it can handle all by itself,
starts from 1.0 + 10"28 to approximately 7.9 + 1028 with 28-29 signifcant digits. Using the
same formula earlier s * m * 10^e, s is 1 or -1 , m from 0 to 2 ^ 96 and e from -28 to 0. The
decimal type unlike foating point does not understand signed zeros, infnities and NaN's.

A decimal is represented by a 96 bit integer scaled by a power of 10. If the absolute value is
less than 1.0m, the number is accurate to the 28th decimal place. Great. But if the value is
greater than 1, the value is exact as per the 28 or 29 digit. The problem with a foat/double
is its ability to represent numbers like 10/3 or .1 in an accurate fashion. These are infnite
fractions and are prone to round of errors. The decimal format does not have these
problems.

For a binary operator if any of the operands is a decimal, the other must either be a decimal
or a integer and not a foating point number in any circumstance. A useful tidbit. All
decimal numbers are rounded of to their nearest possible value and if two values are
equally close, the one that has an even number in the least signifcant or last digit is used.

If a decimal operation produces an answer that is very small for the decimal format after
rounding, unlike a foating point type, the result is zero. However, if the result is too large,
an Overfow Exception is thrown. Unlike foats, the decimal has no qualms about using
exceptions.

Now, to sum up the diferences between foats and decimals. The decimal type has greater
precision or accuracy than the foating point type but at the cost of a smaller range. Thus if
we convert from a foating point to a decimal, we may get an overfow as the foat can store
higher values. Conversion from decimal to foat can cause loss of precision instead. Thus,
there exists no known conversion between foating point types and decimals. You can only
mix them in an expressions by using an explicit cast.

243884994.doc 74 od 276
A bool is antisocial, hating interaction with any other type. A bool cannot be converted to
any other type, not even an integer. It prefers its solitude.

a.cs
class zzz
{
public static void Main()
{
bool i false;
int ; (int)i;
i (bool)0;
}
}

(o'piler +rror
a.cs(5-0!)/ error (%!!,!/ (annot convert t&pe 3bool3 to 3int3
a.cs(1-5)/ error (%!!,!/ (annot convert t&pe 3int3 to 3bool3

When in doubt, use a cast. It does not work with bool. No explicit conversions exist from a
bool to any other type. Read our lips. That's it.

&oxing and 'nboxing

This is a central concept that unifes the type system of C#, which consists of value and
reference types. Boxing is a way by means of which a value type becomes a reference type
and unboxing does the reverse, i.e. it converts a reference type into a value type. It binds
these two types. The resulting reference type is an object naturally.

Boxing, thus, presents a unifed view of the type system where everyone is equal before the
law as an object type. A value type can also implement an interface, and boxing not only
converts a value type to object but also to any interface type implemented by the value type.
In other words, boxing creates an object and copies the values in the value type to the ones
in the object.
Conceptually, we can view a boxing conversion as follows. Assuming we had a value type int
and we want to box this value or convert it into an object. We would internally declare a
class as follows.

a.cs
class intQbo4
{
int 4;
public intQbo4(int t)
{
4 t;
}
}

int i 0.,;
ob;ect b i;

This becomes

int i 0.,;
ob;ect b new intQbo4(i);

243884994.doc 72 od 276
When we write the above lines, internally the compiler can create a class that has any name.
We have chosen int_box as the name of the class. The above is conceptual, it may not
happen the way we've explained it. First, the compiler creates a new object that looks like
the class int_box. This has a constructor that accepts one parameter that simply initializes
the variable x, representing the value type int. Thus, we have now converted an int into an
object. Replace int with the data type you want to promote it to, an Object and the above
explanation then will hold water.

a.cs
class zzz
{
public static void Main()
{
int i 0.,;
ob;ect b i;
if ( b is int)
%&ste'.(onsole.)rite#ine(9true9);
if ( b is ob;ect)
%&ste'.(onsole.)rite#ine(9true09);
if ( b is long)
%&ste'.(onsole.)rite#ine(9true.9);
}
}

*utput
true
true0

The object b has a dual role to perform. Thus, the above classes are not created and in spite
of b being an object it is now also an int.

a.cs
class zzz
{
public static void Main()
{
ob;ect b SaT ;
if ( b is int)
%&ste'.(onsole.)rite#ine(9true9);
if ( b is ob;ect)
%&ste'.(onsole.)rite#ine(9true09);
}
}

*utput
true0

By default, an object is frst only an object and nothing else. In the previous program, we
promoted int i to become a reference type, hence the object b could either be treated as an
object or a value type int at the same time.

a.cs
class zzz
{
public static void Main()
{
int b 00;
if ( b is int)
%&ste'.(onsole.)rite#ine(9true9);
243884994.doc 76 od 276
if ( b is ob;ect)
%&ste'.(onsole.)rite#ine(9true09);
}
}

*utput
true
true0

Also, everyone is derived from object. The earlier example demonstrates this point.

a.cs
class zzz {
public static void Main()
{
444 a new 444(0);
ob;ect b a;
a.4 .;
%&ste'.(onsole.)rite#ine(((444)b).4);
&&& c new &&&(0);
ob;ect d c;
c.4 .;
%&ste'.(onsole.)rite#ine(((&&&)d).4);
}
}
struct 444
{
public int 4;
public 444(int i)
{
4 i;
}
}
class &&& {
public int 4;
public &&&(int i)
{
4 i;
}
}
*utput
0
.

This is the single-most important distinction between a class and a struct or between value
types and reference types. When we equate object b to value type a, we are creating a new
copy of the int in memory and initializing this new memory to the value of the individual
members of the struct. Thus, at the end of the statement, we now have two identical
structures in diferent areas of memory with no linkages between the two. Changing the
value of x from b does not change the value of x in a as they are independent of each other.

However, with reference types, things change a lot. We are not copying the object c but
storing a number in d that signifes where this object starts in memory. Hence, there is only
one object c in memory which can be accessed either using c or d. Thus changing in one will
refect the change in the other.

Unboxing is reversing the above process, that is, converting an object type into value type. A
check has to be performed frst whether the object can be converted to the value type. If the
243884994.doc 77 od 276
check results true then the copy work into the value type should be initiated. Referring to
our earlier example, unboxing would read as.

ob;ect b net intQbo4(0);
int @ ((intQbo4)b).4;

a.cs
class zzz
{
public static void Main()
{
long f 0;
ob;ect b f;
int i (int)b;
%&ste'.(onsole.)rite#ine(i);
}
}

(o'piler +rror
+4ception occurred/ %&ste'.@nvalid(ast+4ception/ :n e4ception of t&pe
%&ste'.@nvalid(ast+4ception was thrown.
at zzz.Main()

A type cannot be unboxed to a new type if it has been boxed earlier as this will throw an
exception at run time. However, it can be converted to the original. The language does a type
check for boxing and unboxing as the type must be the same. A run time type check is
carried out for unboxing operations. So think twice before you unbox.

In the above instance, we started with a long. We boxed it to an object b that is not only an
object but also a long. We then tried to unbox this object into an int and not a long which
causes problems.

a.cs
class zzz
{
public static void Main()
{
long f 0;
ob;ect b f;
long i b;
}
}

(o'piler +rror
a.cs(1-00)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3ob;ect3 to 3long3

We can convert an int into an object without the compiler screaming errors at us. We cannot
do the reverse, that is convert an object b, into a long even though b also stands for a long.
Tough luck.

There is an implicit conversion available from the derived class to the base class. We do not
have to create one as it is already present. A lot more on implicit conversion is explained in
one of the coming chapters.


a.cs
class &&&
243884994.doc 78 od 276
{
}
class 444 / &&&
{
public static i'plicit operator &&& ( 444 a)
{
}
}

(o'piler +rror
a.cs(5-08)/ error (%!88,/ 3444.i'plicit operator &&&(444)3/ userOdefined conversion toBfro' base
class

As such, a conversion is already available so we cannot create one ourselves.

a.cs
class &&&
{
public static i'plicit operator 444 ( &&& a)
{
return new 444();
}
}
class 444 / &&&
{
}

(o'piler +rror
a.cs(,-08)/ error (%!88>/ 3&&&.i'plicit operator 444(&&&)3/ userOdefined conversion toBfro'
derived class

It shows the same error and it is proof that we ignored the earlier error message. We cannot
convert from or to a base/derived class ever as it is handled internally for us.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
a b;
b (444)a;
}
}
class &&&
{
}
class 444 / &&&
{
}

There exists an explicit cast to convert a base class to a derived class and an implicit cast to
convert a derived class to a base class.

#e(nite Assignment

243884994.doc 79 od 276
A variable is defnitely assigned if the compiler can prove that either the variable has been
automatically assigned or it has been the target of at least one assignment. The rules for
defnite assignment are as follows.

Every variable that has been initially assigned is also defnitely assigned.

Before we reach a variable by any possible path, a simple assignment statement is
encountered where this variable is the left operand.

We call a function where the variable is being passed as a left operand

Finally, a local variable includes a variable initializer.

For a structure, the state of a variable is tracked individually as well as collectively. Thus, for
a structure to be defnitely assigned, we need all the individual members to be defnitely
assigned.

There are a large number of reasons for the variable to be defnitely assigned.

Whenever a variable is accessed for its value, it must have been defnitely assigned
otherwise an undefned variable error will take place. An unassigned variable cannot be
accessed for its value. A variable can be initialized in three ways, by being on the left of an
assignment, being passed as an out parameter or fnally appearing as a left operand in a
structure access.

We told you earlier that a ref parameter can be used only in a function invocation if it has
been initialized earlier outside the function. Thus, ref parameters are always defnitely
assigned. Out parameters for a function must be initialized before the function terminates.
Thus, an out parameter will always be considered defnitely assigned when we exit the
function.

a.cs
class zzz
{
public static void Main()
{
}
public void abc()
{
int i-;;
tr&
{
i 0!;
}
catch
{
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
}
}
}

(o'piler +rror
a.cs(08-.5)/ error (%!058/ Hse of unassigned local variable 3i3
a.cs(08-,5)/ error (%!058/ Hse of unassigned local variable 3;3

243884994.doc 83 od 276
In spite of explicitly initializing the variable i in the try block, for the catch the variable i is
not initialized. The compiler for some reason believes that we can enter the catch block
without executing the assignment statement of the try block. Thus, in the catch where we
are using the variables i and j, the compiler believes that they have not been initialized at all
and thus generates the error.

a.cs
class zzz
{
public static void Main()
{
}
public void abc()
{
int i-;;
tr&
{
i 0!;
}
catch
{
}
finall&
{
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
}
}
}

(o'piler +rror
a.cs(07-.5)/ error (%!058/ Hse of unassigned local variable 3i3
a.cs(07-,5)/ error (%!058/ Hse of unassigned local variable 3;3

Ditto for the fnally which always is called at the end of the try. Hence, before you enter a
try-catch-fnally block, please initialize all the variables. The above conclusions, we repeat
have been reached by a process known as static fow analysis.



a.cs
class zzz
{
public static void Main()
{
}
public void abc()
{
int 40-&.;int i;
if ( 4 > . &&(i&) U 0)
%&ste'.(onsole.)rite#ine(9one 9 $ i);
else
%&ste'.(onsole.)rite#ine(9two 9 $ i);
%&ste'.(onsole.)rite#ine(9three 9 $ i);
}
}

(o'piler +rror
a.cs(0.-,8)/ error (%!058/ Hse of unassigned local variable 3i3

a.cs
243884994.doc 81 od 276
class zzz
{
public static void Main()
{
}
public void abc()
{
int 40-&.;int i;
if ( 4 > . VV (i&) U 0)
%&ste'.(onsole.)rite#ine(9one 9 $ i);
else
%&ste'.(onsole.)rite#ine(9two 9 $ i);
%&ste'.(onsole.)rite#ine(9three 9 $ i);
}
}

(o'piler +rror
a.cs(0!-,8)/ error (%!058/ Hse of unassigned local variable 3i3

Two programs, exactly the same, with the only change of && to ||. We are in either case
initializing variable i to variable y in the if statement. Both variables x and y have been
defnitely assigned. What we realize form the above answers is that for a && the if statement
does not complain and for a || the else does not complain. But in both cases after we leave
the if statement, the variable i is not initialized. We understand after we leave the if because
the && and || are short circuit operators and the second condition may not be called
depending on the answer obtained for the frst condition.

If we change the || or && to a single | or &, the error disappears as these are non short
circuit operators and they will always be called. The values of the variables do not seem to
make any diference on the fnal answer. However, if you use a constant like true or false
then the compiler knows whether the frst condition will always be true or false as the case
may be and then will try and optimize everything. There is no reason why it should behave
diferently for a && and a ||. Beyond our comprehension! The documentation says that for
a &&, it will frst execute the assignment and then the other stuf like the embedded
statements following the if. For a || Reverse for the ||. Go fgure it out yourself. We gave up
a long time back!

There are, to be precise, six cases of variables that are initially assigned. These are static
variables, instance variables or public variables in classes, Instance variables of initially
assigned structures, arrays, value and reference parameters. Initially assigned means that
the above variables are guaranteed to have a value. For example, instance variables are
always assigned the default value etc. We have three cases of variables which are
unassigned and whose value cannot be used unless we initialize them frst. These are local
variables created in a function, Out parameters to a function including the this variable of
constructors in a struct and instance variables of initially unassigned structures.

A variable reference is a location in memory that stores the value of a variable such that we
can read or write/change the value of the variable. There are the three places where a
variable reference must be specifed. Nothing else will sufce. These places are the left hand
side or left operand of an assignment, any parameter to a function or constructor denoted as
ref or out.


)onstants

243884994.doc 82 od 276
A constant cannot consist of any data type. They are restricted to the following types only.
These are sbyte, byte, short, ushort, int, uint, long, ulong, char, foat, double, decimal, bool,
string, enum type, or the null type.

We are also restricted in the constructs that a constant expression can use. These are
literals, any sub expressions, casts, the usual gang of suspects in unary operators + - ! and
-, the binary operators +, -, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and => and let
us not forget the single ternary operator ?:. Lest we forget, we can also refer to the other
const members that make up a struct or a class.

Remember the value of a const is evaluated at compile time only. What happens for non-
const expressions at run time, a similar thing happens for a const at compile time. The only
diference is that run time checks always generate exceptions whereas compile time checks
results in an error from the compiler. The default mode is checked, hence, the compiler
entraps any overfows in value. We are allowed to use const in constant declarations, enums,
case statements, goto case, dimension lengths while creating an array and fnally with an
attribute. We also have a free implicit constant conversion that converts an int to a sbyte,
byte, short, ushort, uint, or ulong, assuming that the value fts in the destination type.

)asting

When we write a statement as (x)-y, the compiler is confused as it comes across an
ambiguity. What were are intentions?? Are we trying to subtract x from y, or casting -y to a
type x?? To clarify these doubts, the language follows some more rules of casting which
determines whether a token in a parenthesis is to be considered a cast or not.

If the token follows the rules for a type and not an expression, it is a cast.

The second rule adds on to the frst and states that immediately after we end the
cast we come across a ~, !, (, identifer, literals or a keyword excluding is, then the
expression is evaluated frst and then casted.

The ramifcations of the new rules will be enumerated further. Thus, the compiler is very
strict about treating something as a cast. If it does not follow the above rules, it cannot be a
cast expression.

Thus, the general rule is anything in parenthesis is treated as an expression. If it does not
evaluate to an expression, then and only then a cast is considered. Thus, a cast has the
lowest priority in the eyes of a compiler. In other words, the compiler does not like casts. The
rules have to be syntactically correct. The compiler as usual does not look into the meaning
of what you are trying to say. Remember they are intelligent, but not in the way we think.
You could do the dumbest thing in the world, but as long as you have followed the syntax
rules, the compiler gives you the green light.

If a and b are two identifers, then a.y is the right grammar/way to write a type. This is so in
spite of the fact that a.y cannot represent a type at all. a may be the name of a variable and
not object for all that the compiler cares. If you have understood the earlier rules which we
have not, then (a)b, (a)(b) and (a)(-b) are all casts but (a) -b is not, even if a identifes a type.
Keywords, however, come frst as they cannot be present in an expression all by itself. Thus
if y happened to be a byte for example, the above four possibilities would be casts only. As a
repetition, a cast is the last refuge of a scoundrel.

243884994.doc 83 od 276
The ther dds and Ends

a.cs
class zzz
{
float f 0.0+>!!;
}

(o'piler +rror
a.cs(,-00)/ error (%!86>/ IloatingOpoint constant is outside the range of t&pe 3double3
We have limits on everything in life. A foat may be a very large number, but it yet has a
upper limit. We crossed that limit in the above program.

a.cs
public class zzz
{
private static b&te i !;
public static void Main()
{
if ( i .85)
i!;
}
}

(o'piler )arning
a.cs(5-5)/ warning (%!58./ (o'parison to integral constant is useless; the constant is outside the
range of t&pe 3b&te3

The compiler pounces at anything that he can fnd wrong at the time of compilation. Here it
fnds us comparing a byte to a value that the byte can never aspire to. A value larger than
what a byte in its sweet dreams can ever store 256 larger than the range 1 to 255. Nothing
earth shaking as the if will always be false.

a.cs
public class zzz
{
public void abc()
{
string s 9R'9;
}
}

(o'piler +rror
a.cs(8-0.)/ error (%0!!6/ Hnrecognized escape se<uence

In a string anything that begins with a \ character is called an escape sequence. There are
only a fnite number of such escape sequences defned. \m is not one of them and hence an
error. Think twice before using the \ character.
a.cs
public class zzz
{
public void abc()
{
char s 33;
}
}

(o'piler +rror
a.cs(8-0!)/ error (%0!00/ +'pt& character literal
243884994.doc 84 od 276

A char variable must be initialized to some defned character literal or value and not to a
empty one. Nobody likes emptiness.

a.cs
public class zzz
{
public void abc()
{
char s 3443;
}
}

(o'piler +rror
a.cs(8-0!)/ error (%0!0./ =oo 'an& characters in character literal

A character literal is made up of a single character in a set of single inverted commas.
Internally it takes up two bytes to be stored. We get an error as we have placed two
characters inside the inverted commas.

a.cs
public class zzz
{
long i !4IIIIIIIIIIIIIIIII# ;
}

(o'piler +rror
a.cs(,-0!)/ error (%0!.0/ @ntegral constant is too large

Whenever we exceed the range of values a variable can store, we get an error like above.

a.cs
class zzz
{
int a0!!!!!!!!!!!!!!!!!!!!! ;
}

(o'piler +rror
a.cs(,-1)/ error (%0!.0/ @ntegral constant is too large

Lots of money is bad and a larger value than what a type can hold is even worse!
Chapter 5

Delegates and Events

A delegate is extremely important for C# as it is one of the four entities that can be placed in
a namespace. This makes it shareable among classes. Delegates are fully object oriented as
they entirely enclose or encapsulate an object instance and a method. A delegate defnes a
class and extends System.Delegate. It can call any function as long as the methods
signature matches the delegates. This makes delegates ideal for anonymous invocation. The
methods signature only includes the return type and the parameter list. If two delegates
have the same parameter and return type, that is, they share the same signature, we
consider them as diferent delegates. This chapter will feature these issues using simple
programs.
243884994.doc 82 od 276

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public class aa
{
public delegate void p<r();
void p<r0 ()
{
%&ste'.(onsole.)rite#ine(9p<r09);
}
public void abc()
{
p<r d new p<r(p<r0);
d();
}
}

*utput
p<r0

a is an object that looks like aa. a.abc will call the abc function from this class. Earlier on,
we specifed that new must be followed by the class. We stand corrected as usual here.
Likewise, we can also use a delegate name after new. A delegate is like a property or an
indexer. It is a frst class member of a class. It looks like a function, but has the keyword
delegate in front of it along with the return type of void.

When we use new to instantiate a delegate, we pass it one parameter. This happens to be the
name of a function, namely, pqr1. This function is created in the same class, that is, aa, it
takes no parameters and returns a void. The keyword new obviously creates an object that
looks like a delegate as it was given a delegate as a datatype. d is now an object that looks
like pqr and stores the delegate object of pqr1. Then we use the function invocation syntax
and write d(). To our utter surprise, we have actually executed the function pqr1 without
writing pqr1().

The reasoning here is that d is a delegate object for function pqr1 as we have passed it as a
parameter at the time of creation. Calling d calls function pqr1 instead. This is one level of
abstraction thus helping us write complicated code.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public class aa
{
public delegate void p<r();
void p<r0 ()
243884994.doc 86 od 276
{
%&ste'.(onsole.)rite#ine(9p<r9);
}
public void abc()
{
p<r d new p<r(p<r0);
d(0!!);
}
}

(o'piler +rror
a.cs(06-0)/ error (%086,/ Celegate 3p<r3 does not taAe 303 argu'ents

C# does not let you do whatever your heart desires. The delegate object d that looks like a
pqr is now passed one parameter, that is, 100. As d stands for the function pqr1 and pqr1
does not accept an int as a parameter, an error is shown. This proves that C# does strict
type checking on the code.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public class aa
{
public delegate void p<r();
void p<r0 (int d)
{
%&ste'.(onsole.)rite#ine(9p<r9);
}
public void abc()
{
p<r d new p<r(p<r0);
d();
}
}

(o'piler +rror
a.cs(07-07)/ error (%!0.,/ Method 3aa.p<r0(int)3 does not 'atch delegate 3void aa.p<r()3

In our infnite wisdom, we thought that the above error will vanish if we added an int as a
parameter to our function pqr1. C# however tends to disagree and now gives us diferent
error message.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public class aa
{
public delegate int p<r(int ddd);
243884994.doc 87 od 276
int p<r0 (int dd)
{
return dd W .;
}
int p<r. (int dd)
{
return dd W 0!;
}
p<r d;
public void abc()
{
d new p<r(p<r0);
int d0 d(>.);
%&ste'.(onsole.)rite#ine(d0);
d new p<r(p<r.);
int d. d(>.);
%&ste'.(onsole.)rite#ine(d.);
}
}

*utput
7>
>.!

To eliminate the error message, we added an int parameter to the delegate pqr. This informs
the C# compiler that whoever calls the delegate pqr, will hand it one parameter. The delegate
now returns an int unlike the earlier code where we returned void. Then C# checks whether
the function name given at the time of creation of the delegate accepts one parameter and
returns an int. Finally, when we run the function through the delegate, a last check is made
to see that the right parameters are given.

We have also added a tweeze of lime to our program. We have created one more delegate type
but now it is with another function name as its parameter. The code remains the same. We
keep executing the delegate as d(42), but the function to be called changes each time. This
ofers us greater fexibility in writing dynamic code.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public class aa
{
public delegate int p<r();
void p<r0 ()
{
%&ste'.(onsole.)rite#ine(9p<r9);
}
public void abc()
{
p<r d new p<r(p<r0);
d();
}
}

(o'piler +rror
243884994.doc 88 od 276
a.cs(07-07)/ error (%!0.,/ Method 3aa.p<r0()3 does not 'atch delegate 3int aa.p<r()3

C# is very fussy about a delegate. The return value of the delegate in the defnition says that
it will return an int but the function pqr1 returns a void instead.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public class aa
{
public delegate int p<r(int ddd);
int p<r0 (int dd)
{
return dd W .;
}
int p<r. (int dd)
{
return dd W 0!;
}
public void 4&z( p<r a)
{
int f a(0!);
%&ste'.(onsole.)rite#ine(f);
}
public void abc()
{
p<r d new p<r(p<r0);
4&z(d);
d new p<r(p<r.);
4&z(d);
}
}

*utput
.!
0!!

This function demonstrates a simple fact that a delegate is like a class. We can give objects
that look like classes as parameters to functions. Int, long etc are classes in C#. A delegate
being a data type can be passed as a parameter to a function.

In the frst and second invocation of function xyz we are passing the same delegate type of d.
The frst time it stands for the function pqr and the second time function pqr2. Using the
object a, we are executing a diferent function each time. The code of the function xyz
remains the same. We are indirectly giving it a diferent function each time. The code of
function xyz does not know nor care about the function it is calling.

Writing generic code like the above, helps isolate the code implementation from its
execution. More on these abstract issues in a short while.

a.cs
public class zzz
{
243884994.doc 89 od 276
public static void Main()
{
aa a new aa();
a.abc();
}
}
public class aa
{
public delegate int p<r(int ddd);
int p<r0 (int dd)
{
return dd W .;
}
int p<r. (int dd)
{
return dd W 0!;
}
public void 4&z( p<r a)
{
for ( int i 0; i", ; i$$)
%&ste'.(onsole.)rite(a(i) $ 9 9);
}
public void abc()
{
p<r d new p<r(p<r0);
4&z(d);
d new p<r(p<r.);
4&z(d);
}
}

*utput
. > 5 0! .! ,!

The above program takes the abstract behavior of a delegate a step further. Nothing on earth
stops you from executing a delegate through a loop construct. What we are repeating over
and over again is that a delegate passed as a parameter to a function, does not know the
name of the function it is going to execute at the time of compilation. It will only receive this
information at the time of execution i.e. at run time.

a.cs
public class zzz {
public static void Main()
{
aa a new aa();
a.abc();
}
}
public delegate void p<r();
public class aa
{
void p<r0 ()
{
%&ste'.(onsole.)rite#ine(9p<r09);
}
public void abc()
{
p<r d new p<r(p<r0);
d();
}
}
243884994.doc 93 od 276

*utput
p<r0

pqr is a delegate as it defnes a class that extends System.Delegate.

a.cs
public delegate void p<r();
public class aa / p<r
{
}

(o'pilation +rror
a.cs(.-0>)/ error (%!8!6/ 3aa3 / cannot inherit fro' sealed class 3p<r3

A delegate internally is represented by a class with the same name. In the above case, the
class pqr is implicitly sealed. A sealed class cannot be used in a derivation. A long time ago
we had explained to you that there were four classes you could not derive from. If memory
serves you well, one of them was System.Delegate and it is an abstract class. All delegates
derive from it automatically. We can access all the members of System.Delegate in the
normal way.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public delegate void p<r();
public class aa
{
void p<r0 ()
{
%&ste'.(onsole.)rite#ine(9p<r09);
}
public void abc()
{
p<r d new p<r(p<r0);
d();
%&ste'.(onsole.)rite#ine(d.=o%tring());
}
}

*utput
p<r0
p<r

System.Delegate is also derived from Object and hence using the syntax learnt earlier of
member access, we can call the ToString function of d.

a.cs
public delegate void p<r();
public class aa
{
void abc()
{
243884994.doc 91 od 276
%&ste'.(onsole.)rite#ine(p<r.=o%tring());
}
}

(o'piler +rror
a.cs(5-.5)/ error (%!0.!/ :n ob;ect reference is re<uired for the nonstatic field- 'ethod- or
propert& 3ob;ect.=o%tring()3
We cannot use the name of the delegate pqr with function ToString as it is not static. d being
an instance of the class can be used and will give no errors.

Delegate types come in two sizes, combinable and non-combinable.

a.cs
using %&ste';
delegate void ddd(string s);
class zzz
{
public static void a0(string s)
{
(onsole.)rite#ine(s $ 9 a09);
}
public static void a.(string s)
{
(onsole.)rite#ine(s $ 9 a.9);
}
public static void Main()
{
ddd a- b- c- d;
a new ddd(a0);
b new ddd(a.);
c a $ b;
d a O b;
a(9:9);
b(9?9);
c(9(9);
d(9C9);
d c O a;
d(9+9);
d c O b;
d(9I9);
}
}

*utput
: a0
? a.
( a0
( a.
C a0
+ a.
I a0

This example proves the importance of delegates in C# and they can be made extremely
complex if you so choose to. Objects a, b, c and d are delegate type objects. Object a and b
represent functions a1 and a2 respectively. Thus a("A") and b("B") call functions a1 and a2.
This is shown above.

Object c represents the summation of objects a and b. Obviously we cannot add two delegate
types as they are not numbers. The only rational explanation possible is that we want both
243884994.doc 92 od 276
the delegates to be executed. First, function a1 gets called and then a2. The object d is
initialized to a - b. This will eliminate functions contained in a2 from a1. As we have no
common function in a2, it makes no diference and function a1 is called.

The next series of statements explains the operations better. The object d is made equal to c-
a. This will subtract all the functions that delegate a represents from the object c. The object
c stands for functions a1 and a2, and as a represents function a1, function a1 gets removed
from d. Thus a2 is called. In the last case of c - b, had we executed the object c, both
functions a1 and a2 would get called. But as we are subtracting b i.e. function a2, only a1
gets called.

You can call as many functions you want through delegates.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public delegate int p<r();
public class aa
{
int p<r0 ()
{
%&ste'.(onsole.)rite#ine(9p<r09);
return !;
}
public void abc()
{
p<r d new p<r(p<r0);
p<r e new p<r(p<r0);
p<r f d $ e;
}
}

Two delegates can be combined provided the return type of each is not void. As our newly
defned delegate, pqr, returns an int, we get no errors.

a.cs
public class zzz {
public static void Main()
{
aa a new aa();
a.abc();
}
}
public delegate int p<r(out int i);
public class aa
{
int p<r0 (out int i)
{
%&ste'.(onsole.)rite#ine(9p<r09);
i 0!;
return !;
}
public void abc()
243884994.doc 93 od 276
{
p<r d new p<r(p<r0);
p<r e new p<r(p<r0);
p<r f d $ e;
int A;
f(out A);
}
}
*utput
p<r0
p<r0

Aha! A delegate function can accept output parameters. Hence we see the output displayed
as pqr1.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public delegate int p<r(out int i);
public class aa
{
int p<r0 (out int i)
{
i 0!;
%&ste'.(onsole.)rite#ine(9p<r0 9 $ i);
return !;
}
public void abc()
{
p<r d new p<r(p<r0);
p<r e null;
p<r f d $ e;
int A;
f(out A);
}
}

*utput
p<r0 0!

The same program now works. The only change made is we have initialized one of the
delegates to null. Now the rules of simple delegates come into play. The rules of multi-cast
delegates are more restrictive. This also proves the hypothesis that multi-cast delegates are
not part of the programming language as the compiler did not catch the error. As the
compiler ignored it, it means that it is not part and parcel of the language defnition.

System.Delegate introduces a lot of extra code in the delegate. It is this code that actually
performs error checks and if we break any rules it throws an error at run time. The next
version of System.Delegate and not C# may handle these errors gracefully.

Two diferent delegates can refer to the same method and target object. The plus operator
thus lets you combine delegates and the subtraction operator lets you remove one delegate
243884994.doc 94 od 276
from another. We do have to stop at two delegates only. When we execute a multi-cast
delegate, the efect is similar when invoking an ordered list of non-multicast delegates.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
tr&
{
a.abc();
}
catch ( %&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(9in 'ain9);
}
}
}
public delegate void p<r();
public class aa
{
void p<r0()
{
throw new %&ste'.+4ception();
}
public void abc()
{
p<r d new p<r(p<r0);
d();
}
}

*utput
in 'ain

In the above program, d() calls function pqr1. This function throws an exception. We could
have caught the exception using try catch in function abc itself but we chose not to do so.
Instead, we catch it in main where abc is called. The exception thrown in the function called
by the delegate moves up the ladder until it fnally is caught. If the catch clause is ignored,
it generates a run time error as below.

*uput
+4ception occurred/ %&ste'.+4ception/ :n e4ception of t&pe %&ste'.+4ception was thrown.
at aa.p<r0()
at zzz.Main()

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public delegate void p<r(ref int i);
public class aa
{
void p<r0 (ref int i)
{
243884994.doc 92 od 276
i i $ .;
%&ste'.(onsole.)rite#ine(9p<r0 9 $ i);
}
public void abc()
{
p<r d new p<r(p<r0);
p<r e new p<r(p<r0);
p<r f d $ e;
int A ,;
f(ref A);
}
}

*utput
p<r0 8
p<r0 1

Earlier, we discussed about the out parameters and concluded that they are not allowed in a
multicast delegate. Conversely, passing by ref is permissible in multi-cast delegate
invocations. If the called function changes k, the change is transmitted straight through.
Thus every invocation of pqr1 refers to the same variable k each time.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public delegate void p<r();
public class aa
{
public void p<r0()
{
%&ste'.(onsole.)rite#ine(9p<r09);
throw new %&ste'.+4ception();
}
public void p<r.()
{
%&ste'.(onsole.)rite#ine(9p<r.9);
}
public void abc()
{
p<r d new p<r(p<r0);
p<r e new p<r(p<r.);
p<r f d$e;
f();
}
}

*utput
p<r0

+4ception occurred/ %&ste'.+4ception/ :n e4ception of t&pe %&ste'.+4ception was thrown.
at aa.p<r0()
at p<r.@nvoAe()
at aa.abc()
at zzz.Main()

243884994.doc 96 od 276
If an exception is thrown during a multi-delegate invocation, no more code gets called,
processing simply halts. Everything comes to a standstill. The function pqr1 gets called frst.
As it throws an exception, C# does not call function pqr2, that it was supposed to after
fnishing with function pqr1.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public delegate void p<r();
public class aa
{
void p<r0 ()
{
%&ste'.(onsole.)rite#ine(9p<r09);
}
public void abc()
{
p<r d new p<r(p<r0);
bb b new bb();
p<r e new p<r(b.4&z);
p<r f d $ e;
f();
}
}
public class bb
{
public void 4&z()
{
%&ste'.(onsole.)rite#ine(94&z9);
}
}

*utput
p<r0
4&z

A delegate is not confned to executing code in its own class as its defnition can be placed
outside a class. Here the delegate invocation list invokes two functions, one from the same
class pqr1 and the other function xyz from the class bb. Thus, the delegate not only ignores
the function it is calling but also the class from which the function is called. It believes in
equality thereby does not diferentiate code from one class to another.

E*ents

a.cs
class zzz {
public static void Main() {
&&& l new &&&();
l.abc();
}
}
public class &&&
{
public void abc()
243884994.doc 97 od 276
{
%&ste'.(onsole.)rite#ine(9hi9);
}
}
*utput
hi

We start with a very simple program. In function Main we create an object l that looks like
class yyy. Then we simply call a function abc from it. In this case, we are calling the function
abc directly. There is no entity in between. Lets see how we can call this function indirectly.

a.cs
public delegate void ddd();
class zzz {
public static void Main() {
&&& l new &&&();
l.c $ new ddd(l.abc);
l.a0();
}
}
public class &&&
{
public event ddd c;
public void a0()
{
if (c U null)
abc();
}
public void abc()
{
%&ste'.(onsole.)rite#ine(9hi9);
}
}

*utput
hi

We are doing exactly the same thing as we did earlier but now its with a lot more fanfare. We
started out by creating a delegate ddd. We then instantiated an object that looks like the
delegate ddd and passed it one parameter, the name of the function. In our specifc case it is
abc. As abc resides in a class yyy and we are operating in function Main within class zzz, we
have no choice but to give the full name, l.abc. The object c on the left hand side is not a
delegate but an event. Also the syntax is += and not = as it would give us the following error.

(o'piler +rror
a.cs(8-,)/ error (%!!1!/ =he event 3&&&.c3 can onl& appear on the left hand side of $ or O
(e4cept when used fro' within the t&pe 3&&&3)

The object c has an odd-looking defnition. Earlier we stored the return value of the
instantiation of a delegate in a delegate object, here we have added the keyword event to the
object. Now we call function a1 from Main. In a1, we are checking the value of the object c
before calling abc. If it is not null, then we call the function abc, otherwise we don't.

If we comment out the line new ddd, then the object c does not get initialized. It will retain
its value of null and the function abc will not be executed.

a.cs
public delegate void ddd();
243884994.doc 98 od 276
class zzz
{
public static void Main()
{
&&& l new &&&();
l.c $ new ddd(l.abc);
l.a0();
l.c O new ddd(l.abc);
l.a0();
}
}
public class &&&
{
public event ddd c;
public void a0()
{
if (c U null)
abc();
}
public void abc()
{
%&ste'.(onsole.)rite#ine(9hi9);
}
}

*utput
hi

The frst time we execute l.a1, the object c has a value, it is not null. On the next line we
have -= where we are subtracting the delegate ddd from the event c. The delegate stands for
the function abc in each case. On subtraction, this common function is eliminated and the
event object c now has a value null. Thus we added the function abc in the frst round and
removed it in the second round.

a.cs
public delegate void ddd();
class zzz
{
public static void Main()
{
&&& l new &&&();
l.c $ new ddd(l.abc);
l.c();
}
}
public class &&&
{
public event ddd c;
public void abc()
{
%&ste'.(onsole.)rite#ine(9hi9);
}
}

(o'piler +rror
a.cs(7-,)/ error (%!!1!/ =he event 3&&&.c3 can onl& appear on the left hand side of $ or O
(e4cept when used fro' within the t&pe 3&&&3)

243884994.doc 99 od 276
Unlike a delegate, an event object cannot be used directly. Earlier, we used to execute a
function through the delegate object. That is not permitted with events. Events are employed
for notifcation purposes only.

a.cs
public delegate void ddd();
class zzz
{
public static void Main()
{
&&& l new &&&();
l.c $ new ddd(l.abc);
l.p<r();
}
}
public class &&&
{
public event ddd c;
public void p<r()
{
c();
}
public void abc()
{
%&ste'.(onsole.)rite#ine(9abc9);
}
}

*utput
abc

If you are still ignorant about the use of an event, you are totally blameless. Whatever we
have done so far could have easily been done without events. In the above case, we have
associated an event c with a function abc in class yyy. We then execute function pqr by
giving c(). This executes the event object c. Earlier, a similar thing was not possible as we
were not in the same class. Now as the event object c stands for a function abc, it calls that
function.


The next program demonstrates that we can call as many functions we want.

a.cs
public delegate void ddd();
class zzz {
public static void Main()
{
&&& l new &&&();
l.c $ new ddd(l.abc);
l.c $ new ddd(l.4&z);
l.p<r();
l.c O new ddd(l.4&z);
l.p<r();
l.c O new ddd(l.abc);
l.p<r();
}
}
public class &&&
{
public event ddd c;
243884994.doc 133 od 276
public void p<r()
{
c();
}
public void abc()
{
%&ste'.(onsole.)rite#ine(9abc9);
}
public void 4&z()
{
%&ste'.(onsole.)rite#ine(94&z9);
}
}

*utput
abc
4&z
abc
Hnhandled +4ception/ %&ste'.FullJeference+4ception/ Xalue null was found where an instance of
an ob;ect was re<uired.
at zzz.Main()
This example reminds us of delegates. We are adding two functions to the event c. Thus the
frst time we call pqr, we are only executing the event c. The next time we will call the abc
and pqr functions as they are now bound to the event. A += adds a function and a -=
subtracts/removes a function from the event list. Now when we call the event c, only
function abc will be called as xyz has been removed from the list. Finally we are also
removing abc from the list and the event will have a value null.

Whenever we execute an event that has no functions to notify, a run time exception is
generated. Thus always check if the event has some value and is not null before using it in
your code.

a.cs
public delegate void ddd();
class zzz {
public static void Main()
{
&&& l new 444();
}
}
public class &&&
{
public event ddd c;
public void abc()
{
%&ste'.(onsole.)rite#ine(9abc9);
}
}
public class 444 / &&&
{
public void p<r()
{
c();
}
}

(o'piler +rror
a.cs(.!-0)/ error (%!!1!/ =he event 3&&&.c3 can onl& appear on the left hand side of $ or O
(e4cept when used fro' within the t&pe 3&&&3)
243884994.doc 131 od 276
An event is used to call a function in the same class. Here even though xxx is derived from
yyy, they do not belong to the same class. Hence an event created in class yyy cannot be
used in any class other than yyy, even derived classes are exempted. Normally C# allows
derived classes to inherit the workings of the base class. Events are one of the many
exceptions and hence the error is generated at compile time, not at run time.

a.cs
public delegate void ddd();
class zzz
{
public static void Main()
{
&&& l new &&&();
l.c $ new ddd(l.abc);
444 4 new 444();
l.c $ new ddd(4.4&z);
l.p<r();
}
}
public class &&&
{
public event ddd c;
public void p<r()
{
c();
}
public void abc()
{
%&ste'.(onsole.)rite#ine(9abc9);
}
}
public class 444
{
public void 4&z()
{
%&ste'.(onsole.)rite#ine(94&z9);
}
}


*utput
abc
4&z

The power of events spreads on code across classes. We've used the same code as in the
earlier porgram and given the name of a function, xyz. This function does not belong to yyy
but is taken from xxx. The event call does not seem to bother and acts accordingly.

A similar example was shown with delegates. The same holds true for events too.

a.cs
public delegate void ddd();
public class zzz
{
public event ddd c;
BBpublic ddd c;
public void addQc( ddd a)
{
c $ a;
}
243884994.doc 132 od 276
public void re'oveQc( ddd a)
{
c O a;
}
}

(o'piler +rror
a.cs(5-0,)/ error (%!000/ (lass 3zzz3 alread& defines a 'e'ber called 3addQc3 with the sa'e
para'eter t&pes
a.cs(6-0,)/ error (%!000/ (lass 3zzz3 alread& defines a 'e'ber called 3re'oveQc3 with the sa'e
para'eter t&pes

Each time we create an event object, two functions are automatically created in our class.
These two functions are the name of the event preface by add_ and remove_. The compiler
adds the code for these function. This also means that we cannot have functions with the
same names in the class that has an event. It could also mean that the event code
overwrites the function code.

What we mean is that in the early days of C++, the compiler would frst convert the C++
statements to C code which, would then be executed by the C compiler. Maybe something
similar is taking place here too. Our book C# to IL handles these issues in great depths.

The functions called by the event are called event handlers and they provide notifcations to
our class. The event like a delegate can be called with a million parameters also.

a.cs
class zzz
{
delegate void ddd();
public static void Main()
{
ddd d null;
d();
}
}

*utput
Hnhandled +4ception/ %&ste'.FullJeference+4ception/ Xalue null was found where an instance of
an ob;ect was re<uired.
at zzz.Main()

Whenever we try and execute a delegate, the system does not perform a compile time check,
but at run time it checks for validity. As we have not yet initialized the delegate d, its value is
null. Hence, we get a NullReferenceException thrown back at us.

a.cs
class zzz
{
public delegate void ddd();
public delegate void eee();
public static void Main()
{
zzz a new zzz();
ddd d new ddd(a.abc);
d();
eee e new eee(d);
e();
%&ste'.=&pe t t&peof(eee);
243884994.doc 133 od 276
%&ste'.(onsole.)rite#ine(t.IullFa'e);
if ( e is ddd)
%&ste'.(onsole.)rite#ine(9true9);
}
public void abc()
{
%&ste'.(onsole.)rite#ine(9abc9);
}
}

*utput
abc
abc
zzz$eee

A delegate constructor cannot be only passed the name of a function as a parameter as
we've done for delegate d, but also the name of another delegate. Delegate e is an eee
delegate but is initialized to a ddd delegate, d. This simply makes a copy of the original
delegate. We now have two delegate objects that reference the same function, abc. Internally
eee has nothing to do with ddd. The type of eee does not change due to the copy as copy
cannot change the data type of a delegate.

a.cs
class zzz / &&&
{
public delegate void ddd();
public static void Main()
{
zzz a new zzz();
ddd d new ddd(a.abc);
d();
}
}
class &&&
{
public void abc()
{
%&ste'.(onsole.)rite#ine(9abc9);
}
}

*utput
abc

The function being passed to the delegate is from the base class. This is allowed and we get
no apparent error.

a.cs
class zzz / &&&
{
public delegate void ddd();
public void p<r()
{
ddd d new ddd(base.abc);
d();
}
public static void Main()
{
zzz a new zzz();
a.p<r();
243884994.doc 134 od 276
}
public void abc()
{
%&ste'.(onsole.)rite#ine(9abc zzz9);
}
}
class &&&
{
public void abc()
{
%&ste'.(onsole.)rite#ine(9abc &&&9);
}
}

*utput
abc &&&

We can also use the keyword base to call the function from the base class. If you remember,
base calls code from the base class and not from the derived class. This is in spite of what
the documentation says and we quote verbatim 'If the method group resulted from a base-
access, an error occurs'. There is no way known to man that can change the method
associated with a delegate once the delegate has been created. It remains the same for the
entire lifetime of the delegate. The parameter to a delegate creation cannot be a constructor,
indexer, property or obviously a user defne operator even though they carry code. We are
left with only one choice as a parameter, a method.

a.cs
class zzz
{
public delegate void ddd();
public static void Main()
{
ddd d new ddd(zzz);
}
}


(o'piler +rror
a.cs(5-01)/ error (%!006/ 3zzz3 denotes a 3class3 which is not valid in the given conte4t

The error message should have been far more clearly stating in a loud bold voice, 'Thou shall
not give a Constructor as a parameter to a delegate ' instead of a more generic one which
does not hit the nail on the head.

The ther dds and Ends

a.cs
public delegate void ddd();
public class zzz {
public event ddd d0{
add {
return null;
}
}
}
(o'piler +rror
a.cs(,-07)/ error (%!!58/ 3zzz.d03 / event propert& 'ust have both add and re'ove accessors

243884994.doc 132 od 276
Whenever we create an event property, unlike a normal property, an event property must
implement both the add and the remove accessors.

a.cs
public class ddd
{
}
public class zzz
{
public event ddd d0
{
add { return null;}
re'ove {}
}
}

(o'piler +rror
a.cs(5-07)/ error (%!!55/ 3zzz.d03/ event 'ust be of a delegate t&pe

An event must be the data type of a delegate and not of a user defned type. Here ddd must
only be a delegate otherwise error no CS0066 results. That is why we frst explained a
delegate and then an event. Events follow delegates and thus the designers of C# frst
created delegates and as an afterthought events.

a.cs
delegate void ddd();
interface iii {
event ddd d new ddd();
}

(o'piler +rror
a.cs(,-00)/ error (%!!57/ 3iii.d3/ event in interface cannot have initializer

For an interface always follow a simple rule. We cannot place any code in a interface,
including initializing a variable/object. Interfaces can only have defnitions, no code please.

a.cs
public delegate void ddd();
public interface iii
{
event ddd d0 {
re'ove {}
add {return null;}
}
}

(o'piler +rror
a.cs(>-00)/ error (%!!56/ 3iii.d03/ event in interface cannot have add or re'ove accessors

In an interface, even a whif of code is not allowed. In an accessor we would obviously like to
place a lot of code. In an interface, an event or a property cannot have any accessor code
and hence the {} are bad syntax.

a.cs
public delegate void ddd();
public interface iii
{
event ddd d0
{
243884994.doc 136 od 276
re'ove { }
add { }
}
}

(o'piler +rror
a.cs(>-00)/ error (%!!56/ 3iii.d03/ event in interface cannot have add or re'ove accessors

Even accessors are not allowed. A property in an interface behaves a little diferently and
would not have given an error in the above case.
Outside the class an event is declared in, an event can only add or subtract a reference.

a.cs
public delegate void ddd();
public class zzz
{
public event ddd e0;
public static void Main() {
}
}

(o'piler )arning
a.cs(>-07)/ warning (%!!51/ =he event 3zzz.e03 is never used

The compiler considers an event to be very important so if it is not being used in a program,
but declared it gives you a harmless warning. One more warning number used up. The error
and warning numbers run concurrently. They are no overlaps and share the same numbers.

a.cs
public delegate void ddd();
interface iii {
event ddd e0;
}
class zzz / iii
{
event ddd iii.e0()
{
}
}

(o'piler +rror
a.cs(7-0>)/ error (%!!10/ :n e4plicit interface i'ple'entation of an event 'ust use propert&
s&nta4
a.cs(7-08)/ error (%08.!/ (lass- struct- or interface 'ethod 'ust have a return t&pe

Whenever we implement an event that has been previously declared in an interface, we have
to use the property syntax of a get and set. A normal function syntax will not sufce. Events
and interface work in an odd way together.

a.cs
delegate void ddd (int i);
class zzz {
public static void Main()
{
ddd d new ddd(0.);
}
}

(o'piler +rror
243884994.doc 137 od 276
a.cs(8-01)/ error (%!0>6/ Method na'e e4pected

A delegates only job is to call another function. The name of the function is handed over to
the delegate at the time of creation of the delegate object. We are passing a number and not
a method name. When calling the function through the delegate only, we pass it an int, as
suggested.

a.cs
public delegate void ddd();
class zzz
{
ddd d;
public static void Main()
{
zzz a new zzz();
a.d.@nvoAe();
}
}

(o'piler +rror
a.cs(7-8)/ error (%08,,/ @nvoAe cannot be called directl& on a delegate

9ou cannot use the (nvoke method to ca a dee"ate directy. )he dee"ate can ony 'e used in
one standard #ay as sho#n earier. )here is no other #ay out.
Chapter 6

Conversions !mplicit and E"plicit

Most of us imply all the time and are rarely explicit.

The C# programming language has exactly 78 keywords in all. But there are only two types
of conversions, implicit and explicit. As the name suggest, an explicit conversion has to be
specifcally supplied by using a cast and at all other times, the conversion is considered to
be implicit. Conversion comes into play when converting from a source type to a target type,
which is fnally the return value of the operator.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine(a$b);
}
}
class &&&
{
public i'plicit operator 444 (int b)
{
return new 444();
}
}
class 444
{
}
243884994.doc 138 od 276

(o'piler +rror
a.cs(0.-7)/ error (%!885/ HserOdefined conversion 'ust convert to or fro' the enclosing t&pe

The rules for user-defned conversions are extremely simple. They have to accept one
parameter, similar to unary operators, and must be of the same data type as of the class.
Under normal circumstances, conversion operators do not have a return type as it assumes
the return type to be that of the operator type. The return data type should directly or
indirectly be the data type that is being enclosed within the brackets.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
}
}
class &&&
{
public static i'plicit operator 444 (&&& b)
{
return new &&&();
}
}
class 444
{
}

At times, we can catch the compiler napping. We are returning a yyy object and not an xxx.
As per the compiler rules, we should have received an error as we are to return an xxx,
whereas we are returning a yyy object. May be, as all classes are derived from object, so the
compiler either misses the error or condones it.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
}
}
class &&&
{
public static i'plicit operator 444 (&&& b)
{
return new aaa();
}
}
class 444
{
}
class aaa
{
}

(o'piler +rror
a.cs(0,-7)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3aaa3 to 34443
243884994.doc 139 od 276

We believe the above is a special case. Here we are returning an aaa object instead of xxx.
The compiler tries to convert the aaa object to an xxx object and realizes there is no such
conversion possible in class yyy. Hence, the error. To remove the error, place an operator xxx
in class aaa, which will convert the aaa object into an xxx like object. It will make an
exception only if the return type is the same class that the code resides in. We yet are frm
in our belief that someone somewhere goofed up.


a.cs
class &&& {
public static i'plicit operator &&&(&&& b)
{
return new &&&();
}
}

(o'piler +rror
a.cs(,-08)/ error (%!888/ HserOdefined operator cannot taAe an ob;ect of the enclosing t&pe and
convert to an ob;ect of the enclosing t&pe

Thus we can only convert from one type to another and not to the same type again. It does
not make any sense even to a madman why we would like to take a yyy object and convert it
back again to a yyy object. Also, these conversion operators must lie in the same class and
not in any other class.

a.cs
public class zzz {
public static void Main()
{
&&& a new &&&();
444 b new 444();
b a;
a b;
}
}
class &&&
{
public static i'plicit operator 444 (&&& b)
{
%&ste'.(onsole.)rite#ine(9op 4449);
return new 444();
}
}
class 444
{
public static i'plicit operator &&& (444 b)
{
%&ste'.(onsole.)rite#ine(9op &&&9);
return new &&&();
}
}

*utput
op 444
op &&&

The code to convert an xxx object to a yyy must reside in the class yyy only. If we create
such an operator, it will work on a=b, as the xxx object b can now be converted into a yyy
243884994.doc 113 od 276
like object. However, b = a will give an error unless we have an impicit operator yyy in class
xxx. If there is no such conversion available in class xxx to convert a yyy object to an xxx
object, a compiler error is generated. This conversion cannot reside in yyy or any other class,
it has to be in the xxx class only.

a.cs
public class zzz {
public static void Main() {
&&& a new &&&();
444 b new 444();
b a;
a b;
}
}
class &&&
{
public static i'plicit operator 444 (&&& b)
{
%&ste'.(onsole.)rite#ine(9op 4449);
return new 444();
}
public static i'plicit operator &&& (444 b)
{
%&ste'.(onsole.)rite#ine(9op &&&9);
return new &&&();
}
}
class 444
{
}
*utput
op 444
op &&&

Should we apologize to the compiler? No. Did we lead you up the garden path? No. Every
language has certain quirks. We want to convert a yyy object to an xxx object and vice versa.
C# felt it to be too cumbersome to write the code in two separate classes. So it made a one
time exception to all its rules. You can have a conversion operator take a single parameter
other than the class data type. We have thus broken one golden rule in operator yyy within
class yyy.
Class yyy contains a function that accepts one parameter, not a yyy object but an xxx
object. The code is much more compact where logically relevant code is place together.

Remember there is always a method behind the madness and at times breaking rules makes
the world go round better.

a.cs
class &&& {
public static i'plicit operator 444 (&&& b)
{
return new 444();
}
}
interface 444
{
}

(o'piler +rror
243884994.doc 111 od 276
a.cs(,-08)/ error (%!88./ 3&&&.i'plicit operator 444(&&&)3/ userOdefined conversion toBfro'
interface

We cannot convert to and from an interface. The above restriction is due to the fact that an
interface and a class difer in concept. A class has code whereas an interface doesn't.
Similar to water and oil that do not mix and match, we cannot convert from two difering
concepts.



a.cs
class &&& / 444
{
public static i'plicit operator 444 (&&& b)
{
return new 444();
}
}
class 444
{
}

(o'piler +rror
a.cs(,-08)/ error (%!88,/ 3&&&.i'plicit operator 444(&&&)3/ userOdefined conversion toBfro' base
class

An object that looks like yyy also looks like xxx as yyy is derived from xxx. Thus there is
already a way for a yyy object to scale down and become an xxx object. This is built in the
inheritance structure. We do not need an operator to do what already happens by default.

Converting to a base class is an error as we try to duplicate what the compiler has already
done for us. In the same vein, we cannot convert to an object as the Object class is the base
class for all classes. All classes implicitly convert to an Object class and these conversions
are already predefned. We are also not allowed to override a predefned conversion. The
signature of the operator is its name, return type and parameter type. Unlike operators, the
return type is part of the signature. The keyword implicit and explicit are not part of the
signature.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
b a;
}
}
class &&&
{
public static i'plicit operator 444 (&&& b)
{
throw new %&ste'.+4ception();
return new 444();
}
}
class 444
{
243884994.doc 112 od 276
}

*utput
+4ception occurred/ %&ste'.+4ception/ :n e4ception of t&pe %&ste'.+4ception was thrown.
at &&&.opQ@'plicit(&&& b)
at zzz.Main()

A user-defned operator is not supposed to throw an exception. Remember an exception is
thrown when something goes wrong. We are not supposed to lose information in a user-
defned conversion. By convention, an implicit operator never throws an exception, whereas
an explicit one is allowed to. You are permitted to foat a convention at your own peril.

a.cs
public class zzz
{
public static void Main()
{
}
}
class &&&
{
public static i'plicit operator 444 (&&& b)
{
return new 444();
}
public static 444 opQ@'plicit(&&& b)
{
return new 444();
}
}
class 444
{
}

(o'piler +rror
a.cs(0,-06)/ error (%!000/ (lass 3&&&3 alread& defines a 'e'ber called 3opQ@'plicit3 with the sa'e
para'eter t&pes

By throwing the above exception, we now know that our conversion operator is renamed to a
function called op_Implicit with one parameter, a yyy object in the above case. What if we
have two such conversion operators?

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
aaa b new aaa();
444 c new 444();
tr&
{
b a;
}
catch ( %&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(e);
}
tr&
{
243884994.doc 113 od 276
c a;
}
catch ( %&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(e);
}
}
}
class &&&
{
public static i'plicit operator 444 (&&& b)
{
throw new %&ste'.+4ception();
return new 444();
}
public static i'plicit operator aaa(&&& c)
{
throw new %&ste'.+4ception();
return new aaa();
}
}
class 444
{
}
class aaa
{
}

*utput
%&ste'.+4ception/ :n e4ception of t&pe %&ste'.+4ception was thrown.
at &&&.opQ@'plicit(&&& c)
at zzz.Main()
%&ste'.+4ception/ :n e4ception of t&pe %&ste'.+4ception was thrown.
at &&&.opQ@'plicit(&&& b)
at zzz.Main()

Here is what surprises us. We always thought that two functions could not ever have the
same name. Here the compiler has converted the implicit functions to have the same name
and if we had not changed the name of the object in the parameter list from b to c, they
would have been identical. In the earlier example, where we had a function called op_
Implicit, the compiler, gave us an error, but in the above example, the compiler actually
scales down to two functions with the same name.

How does it internally separate them, we have no idea at all. Life is a drag. One set of rules
for the compiler, another set for us. The compiler can break rules with impunity, we cannot.

There are seven types of implicit conversions, which are identity, implicit numeric, implicit
enumeration, implicit reference, boxing, implicit constant expression and fnally user-
defned implicit conversions.

We are allowed to equate any type to our user-defned type. This type of conversion is called
an identity conversion where it lets an entity of a particular type to be converted to the type
we want.

a.cs
public class zzz
{
public static void Main()
{
243884994.doc 114 od 276
&&& a new &&&();
sb&te i 0!;
%&ste'.(onsole.)rite#ine(a$i);
}
}
class &&&
{
public static string operator $ (&&& b-int 4)
{
return 9int 9 $ 4;
}
public static string operator $ (&&& b-short 4)
{
return 9short 9 $ 4;
}
public static string operator $ (&&& b-deci'al 4)
{
return 9deci'al 9 $ 4;
}
}

*utput
short 0!

An implicit numeric conversion converts an sbyte to short, int, long, foat, double, or
decimal. The above line is courtesy Mr Documentation. It simply means that whenever the
compiler sees a byte, it will try and fnd a matching function or operator that accepts a byte
as a parameter. In this case, there is no operator + that accepts a byte as a parameter. So,
the compiler now looks for another function that accepts a short as a parameter. If it fnds
one, it promotes the byte to a short, rolls up its sleeves and calls the function. Thus, we get
short 10 displayed as the result.

C# does all of this without taking our permission. Thus it is called an implicit numeric
conversion because it happens silently in the background without our knowledge. When we
remove the code of the operator + that accepts a short, we get the following answer.

*utput
int 0!

The answer is very intuitive. The compiler starts the whole process explained above once
again. It frst tries to fnd a function that accepts a short. No luck. The compiler does not
give up and now tries to fnd one that accepts an int. Bingo, it fnds one so it executes the
function.

If we now remove the plus operator with the int, then only the one with decimal stays. As
per the above rules, a match will be found and the function will be called.

The order of conversion is as decided by the compiler. We cannot change the above order, all
that we can do is gracefully accept it and carry on with life. If you notice, this order is from
small to large in terms of memory allocation by the compiler. The order makes a lot of sense
now.

a.cs
public class zzz
{
public static void Main()
{
243884994.doc 112 od 276
&&& a new &&&();
sb&te i 0!;
%&ste'.(onsole.)rite#ine(a$i);
}
}
class &&&
{
public static string operator $ (&&& b-deci'al 4)
{
return 9deci'al 9 $ 4;
}
public static i'plicit operator string ( &&& a)
{
return 9hi9;
}
}

*utput
deci'al 0!

Strange are the ways of a compiler. In the above case, we presumed the compiler would face
a dilemma. It could either convert the byte to a decimal, or convert a yyy object to a string
and then call the plus operator. It prefers the frst option as it means fewer steps to be
carried out. Remove the plus operator totally and the second option will get executed. The
string operator of the class yyy is called which returns a string This is then concatenated
with 10 to display hi10.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
sb&te i 0!;
%&ste'.(onsole.)rite#ine(a$i);
}
}
class &&&
{
public static string operator $ (&&& b-deci'al 4)
{
return 9deci'al 9 $ 4;
}
public static i'plicit operator string ( &&& a)
{
return 9hi9;
}
public static i'plicit operator b&te ( &&& a)
{
return .0;
}
}

*utput
deci'al 0!

The compiler has a one-track mind. It always prefers an implicit numeric conversion. It
could have converted the yyy object to a byte and then called the predefned plus to add two
numbers. It did not do it then for a string, it will not do it now either for two numbers. It has
243884994.doc 116 od 276
its own preferences on doing things its way. If we remove the operator plus code from the
class, we get the following error.

(o'piler +rror
a.cs(1-.5)/ error (%!!,>/ *perator 3$3 is a'biguous on operands of t&pe 3&&&3 and 3b&te3

Here the compiler has no preferences over calling the string or the byte conversion
operators. Hence, it fags an error whenever it cannot make up its mind or is in two minds.
The documentation came up with other rules for the other data types. We have copied them
and would advise you to remember them on a rainy day.

Byte to short, ushort, int, uint, long, ulong, foat, double, or decimal. Short to int, long,
foat, double, or decimal. Ushort to int, uint, long, ulong, foat, double, or decimal. Int to
long, foat, double, or decimal. Uint to long, ulong, foat, double, or decimal. Long to foat,
double, or decimal. Ulong to foat, double, or decimal. Char to ushort, int, uint, long, ulong,
foat, double, or decimal and fnally Float to double.

We have no choice but to have a piece of paper with the above rules written down as a ready
reckoner. We feel very guilty when we copy-paste from the documentation and avoid doing it
like the plague. However, we have no choice as rules are rules are rules!!.

Finally, a char stores unicode characters which are fnally numbers. Working with chars is a
one-way street. You are allowed to convert a char to another data type but not vice versa. For
instance, a byte cannot be converted to a char.

An implicit enumeration conversion lets the number zero be converted to any enum type.
This was expected, as an enum is nothing but a glorifed number with a name.

$mplicit Re+erence con*ersions

The above conversions are what we have been talking about throughout this book. The only
diference is that we are pegging a label or a name to what we already know. All reference
objects can be scaled down to an object without a murmur. A derived class can be scaled
down to a base class or a base interface. Ditto for interfaces derived from each other. Any
array type is scaled to System.Array and delegate to System.Deleagte. In turn, an array or a
delegate can also become an interface System.IClonable. The null type can be used to
initialize any reference object.

Arrays at times pose a small problem. We can equate arrays only if they are of the same
dimension and they store reference types. Then they become simple objects and a
conversion must exist to convert one to the other. These do not require any compile time
checks and do not change the value of the object, they may change the data type only.

Implicit constant conversions only deal with constants. They are very simple in nature. An
int can be converted to a sbyte, byte, short, ushort, uint, or ulong provided the range of the
destination is not exceeded. In the same way , a ulong can be converted to a long. How
boring! Makes us feel sleepy writing all this original travail. How many times did you yawn?
We, a hundred thousand times!

Explicit )on*ersions

243884994.doc 117 od 276
Explicit conversions are made up of all implicit conversions, explicit numeric conversions,
explicit enumeration conversions, explicit reference conversions, explicit interface
conversions, unboxing conversions and fnally user-defned explicit conversions. Remember,
for the last time, an explicit conversion only exists in a cast expression. It is like beauty, it
can only lie in the eyes of the beholder.

An explicit conversion has a large number of useful properties. It may or may not succeed.
There is no way to prove that explicit conversions will always succeed and that is why they
are placed in the explicit category. We may lose some information on our path to conversion.
Perfectly acceptable. They also have vision and can convert from a large range of domain
types. We can have as many redundant casts as we like in a single expression.

a.cs
public class zzz {
public static void Main() {
&&& a new &&&();
&&& b (444)(aaa) a;
}
}
class &&&
{
public static i'plicit operator aaa( &&& a)
{
%&ste'.(onsole.)rite#ine(9op aaa9);
return new aaa();
}
}
class aaa
{
public static i'plicit operator 444( aaa a)
{
%&ste'.(onsole.)rite#ine(9op 4449);
return new 444();
}
}
class 444
{
public static i'plicit operator &&&( 444 a)
{
%&ste'.(onsole.)rite#ine(9op &&&9);
return new &&&();
}
}
*utput
op aaa
op 444
op &&&

In the above case, we have come a full circle. We are frst converting a yyy object into an aaa
object then to an xxx object and fnally, once again to a yyy object. We can have as many
casts as we want on a single line and the compiler will do our bidding for us. It does not
check the veracity of our casting nor does it realize that we are coming back to square one.
Thus, multiple redundant casts are allowed always.

Explicit numeric con*ersions

a.cs
public class zzz
243884994.doc 118 od 276
{
public static void Main()
{
char i 3:3;
short b 58;
i b;
}
}

(o'piler +rror
a.cs(1-8)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3short3 to 3char3

We told you earlier that a char cannot be converted to any other data type but the reverse
was possible. Now we want the above conversion to work, by hook or crook.

a.cs
public class zzz
{
public static void Main()
{
char i 3:3;
short b 58;
i (char)b;
%&ste'.(onsole.)rite#ine(i);
}
}

*utput
:

The only way out is a simple cast. Something that was not possible implicitly is now
available with a simple cast. This is the beauty of an explicit conversion. We are going a step
further than what the implicit conversion does, hence these conversions are over and above
the implicit ones. Explicit conversions are more powerful and useful than implicit ones.

One more round of copying from the documentation just to make the book complete. These
are the conversions that are available to us if and only if we use a cast. They are as follows
verbatim. Sbyte to byte, ushort, uint, ulong, or char. Byte to sbyte and char. Short to sbyte,
byte, ushort, uint, ulong, or char. Ushort to sbyte, byte, short, or char. Int to sbyte, byte,
short, ushort, uint, ulong, or char. Uint to sbyte, byte, short, ushort, int, or char. Long to
sbyte, byte, short, ushort, int, uint, ulong, or char. Ulong to sbyte, byte, short, ushort, int,
uint, long, or char. From char to sbyte, byte, or short. Float to sbyte, byte, short, ushort, int,
uint, long, ulong, char, or decimal. Double to sbyte, byte, short, ushort, int, uint, long,
ulong, char, foat, or decimal. Decimal to sbyte, byte, short, ushort, int, uint, long, ulong,
char, foat, or double.

It breaks our heart to cut-and-paste from the documentation and fll up pages of our the
book. Do we have a choice? No. Should you remember all the above rules ? Never, not even
on a rainy day!

Thus, we are able to convert from one numeric type to another as they are covered by either
implicit or explicit conversions. The only way to remember the above rule is to try and
convert one numeric type to another. If we get an error then simply cast. As we are
permitted to convert from one type to another, we must be prepared to catch exceptions
being thrown at run time. Also, some information may be lost due to the conversion. The
exception gets thrown depending upon the context, checked or unchecked.
243884994.doc 119 od 276

a.cs
public class zzz
{
public static void Main()
{
b&te i (b&te),.5;
%&ste'.(onsole.)rite#ine(i);
}
}

*utput
,

A foat, double or decimal type can be cast to an integer type. However, the compiler will not
round of the double to an integer but instead remove everything after the decimal place.
The documentation calls this rounding towards zero.

By now, you must have realized that we are reading the documentation with a fne
toothcomb. It is, after all, the last word on the C# programming language. The last word may
not read like a snazzy detective novel, but nor does our book. The more we criticize the
documentation on grounds of legibility, readability, etc, the more we will sound like the pot
calling the kettle black. People staying in glass houses should not throw stones and we have
already thrown enough.

Enums work the way for conversions as expected and no point cutting more trees to explain
what they do. We honestly feel you can spend a lifetime writing C# code and yet not use an
enum ever. The Java programming language does not use enums and nobody misses them
ever.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
b a;
b (444)a;
a (&&&)b;
a b;
}
}
class &&&
{
public static i'plicit operator 444( &&& a)
{
return new 444();
}
}
class 444 {
}

(o'piler +rror
a.cs(6-5)/ error (%!!,!/ (annot convert t&pe 34443 to 3&&&3
a.cs(0!-8)/ error (%!!.6/ (annot i'plicitl& convert t&pe 34443 to 3&&&3

243884994.doc 123 od 276
We have created a standard implicit conversion of a yyy object to an xxx object, as usual, by
using the operator xxx. An explicit operator is not created as we can use only one of the
modifers either implicit or explicit. However, each time we use the implicit modifer we get a
free explicit one also. Thus the line b = (xxx) a does not give us any error.

It is better to use an implicit modifer as it also doubles up for an explicit modifer. Thus, we
now understand that when we equate a byte to an int, it is an implicit modifer as it works
with and without a cast. Alas, the reverse is not true.

We wrote a conversion from class yyy to class xxx and not from xxx to yyy. The last two lines
of main give us an error. Nothing in life is free and thus we have to actually write the
conversion ourselves.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
b a;
b (444)a;
}
}
class &&&
{
public static e4plicit operator 444( &&& a)
{
return new 444();
}
}
class 444
{
}

(o'piler +rror
a.cs(1-8)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3&&&3 to 34443

Now that we have asked for the conversion to be explicit, the statement b = a will give an
error. This is because we have not used a cast and are implying a conversion. The cast on
the next line, however, passes the muster as it invokes the explicit operator. To sum up in a
sentence, the implicit conversion operator is a superset of the explicit operator. An explicit
modifer is to be used only when the programmer is to be compelled to use a cast.

Conversions for the last time - we promise.

a.cs
public class zzz
{
public static void Main()
{
}
}
class &&&
{
public static i'plicit operator 444( &&& a)
{
return new aaa();
243884994.doc 121 od 276
}
}
class 444
{
}
class aaa
{
}

(o'piler +rror
a.cs(00-7)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3aaa3 to 34443

In the above program, we have the operator xxx that accepts one parameter, yyy, as we are
in class yyy. This has been made abundantly clear in the past. What we were vague about
was the return type. Nowhere in the world does it say that the user defned conversion
operator xxx has to return an xxx type. All that it said is that it should return an object that
can be converted to an xxx. It does not have to, we repeat again, return an xxx object. The
class aaa does not convert from an aaa object to an xxx object and thus the error is
generated.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
444 4 new 444();
4 a;
}
}
class &&&
{
public static i'plicit operator 444( &&& a)
{
%&ste'.(onsole.)rite#ine(9op 444 &&&9);
return new aaa();
}
}
class 444
{
}
class aaa
{
public static i'plicit operator 444( aaa a)
{
%&ste'.(onsole.)rite#ine(9op 444 aaa9);
return new 444();
}
}

*utput
op 444 &&&
op 444 aaa

We now get no errors even if the operator xxx from class yyy returns an aaa object. This is
because the compiler can now convert to an xxx using the operator xxx in class aaa. The
end result brings an xxx object into being. If the object is being indirectly returned then you
can discount returning the same type as the operator within a class.

243884994.doc 122 od 276
a.cs
class zzz
{
public static void Main()
{
zzz a new zzz();
&&& b new &&&();
444 c new 444();
aaa d new aaa();
a.abc(b);
%&ste'.(onsole.)rite#ine();
}
public void abc(444 4)
{
%&ste'.(onsole.)rite#ine(9abc 4449);
}
public void abc(aaa 4)
{
%&ste'.(onsole.)rite#ine(9abc aaa9);
}
}
class &&&
{
public static e4plicit operator 444 ( &&& a)
{
%&ste'.(onsole.)rite#ine(9op 4449);
return new 444();
}
public static i'plicit operator aaa( &&& a)
{
%&ste'.(onsole.)rite#ine(9op aaa9);
return new aaa();
}
}
class 444
{
}
class aaa {
}

*utput
op aaa
abc aaa

The function abc is overloaded to accept either an xxx or an aaa object. We are passing it
however, a yyy object. The compiler will frst check whether there is some way to convert a
yyy object to either an xxx or an aaa object. It fnds two such operators in the class yyy.
Therefore it does not give us an error at this point, as the two operators are not at the same
level of importance for the compiler. One of them, the operator aaa is implicit so the
compiler gives it a greater importance than the explicit operator. Hence, operator aaa gets
called.

If both were implicit, then we would get an error as the compiler in confused state will not
know which operator to call. If both were explicit, a diferent error results and the compiler
will not call any. In one case, it is a problem of plenty, and in the second case, a problem of
scarcity.
Thus an implicit can stand in for an explicit but not vice versa. The above statement means
that if we explicitly cast the object in the function abc to an aaa i.e. a.abc((aaa)b), it would
yet call the implicit operator aaa in class yyy. However, the converse is not true. If we
243884994.doc 123 od 276
explicitly cast parameter b to a xxx, it will always call the operator xxx in class yyy
irrespective of the modifer explicit or implicit. In case you have a tendency to forget, look at
it in this way; an implicit is the elder, more intelligent brother and explicit is the younger,
less developed brother. Implicit is the superset of explicit.

a.cs
public class zzz
{
public static void Main()
{
}
}
class &&&
{
public static i'plicit operator 444( &&& a)
{
return new aaa();
}
}
class 444
{
}
class aaa
{
public static i'plicit operator bbb( aaa a)
{
return new bbb();
}
}
class bbb
{
public static i'plicit operator 444( bbb a)
{
return new 444();
}
}

(o'piler +rror
a.cs(00-7)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3aaa3 to 34443

However, the compiler does not go far enough for us. It stops short. For various reasons
known only to the compiler, it does not look beyond a class for user-defned conversions.
This means that in the above case an error will be generated as within aaa, it will only check
for an operator that can convert an aaa object into an xxx object.

But when the compiler digs deeper, it fnds a way out. The compiler can covert an aaa object
to a bbb object and then convert that bbb object to an xxx object using the operator xxx in
class bbb. For some weird reasons, it simply looks at the class and as it has no operator
xxx, it simply spews out an error. It does not look beyond the confnes of class aaa thus
proclaiming that it has a myopic view.

By following the above approach, it makes it easier for the programmer who wrote the
compiler, as he does not have to make it any more intelligent. We would have preferred a
more intelligent companion or compiler on our side.

Before opting for a conversion method, the compiler follows a predefned set of rules. It frst
fgures out the guest list. This is a list of classes that can contribute an operator. It
comprises of the source and target classes as well as their base classes. From now on,
243884994.doc 124 od 276
unless otherwise stated, a struct can also be used seamlessly for a class. Then, to choose a
user defned function, it has to match two conditions. First and foremost, the
argument/parameter type of the source must match that of the operator and more
important, the return value of the operator must be convertible to the target type.
Now comes the problem. The compiler needs to determine which conversion to use, if there
are more than one matches. The one that most closely matches is the one chosen, bearing
in mind, that we do not go astray and look into a million classes. Hence, a conversion may
have to convert the source type to match the parameter of the operator, then execute the
operator, and fnally convert to the return type if necessary.
Some big words again. We want to convert from type A to type B both don't fall under
interface types. If we use implicit conversion, then type A is said to be encompassed by type
B and B encompasses type A. Very well said !

Numeric Promotions

Take a look at this:

int operator *(int x, int y);uint operator *(uint x, uint y);long operator *(long x, long y);ulong
operator *(ulong x, ulong y);foat operator *(foat x, foat y);double operator *(double x,
double y);decimal operator *(decimal x, decimal y);

We cheated. We found out that the multiplication operator * is not defned once but
overloaded seven times as seen above. If you observe the above seven carefully, you will
realize that there is no overload to multiply a byte and a short. If we had all permutations
and combinations of numeric types, we would have to write a million such operator
overloads. Coming back to our earlier example. The compiler would convert our byte and
short to an int as they both can be converted to ints. One more reason to this is that the
frst operator on the above list accepts two ints. However, if we had to multiply a double and
an int, both would be converted to a double and the second last operator would be called.
The compiler will try its best to fnd a match higher in the hierarchy in case of any in
dissimilarities.

Unary operator promotions apply only to three operators +, - and ~. All that they do is
convert sbyte, byte, short, ushort or char to an int. As simple as that. Thus if we write a
unary plus operator on a byte, the byte gets automatically promoted to an int. The unary -
goes a step further, it also converts a uint to a long.

Binary promotions also work in a similar way. There are however many more binary
operators than any of the other two types of operators. To jog your memory, we reproduce
the binary operators once again. +, -, *, /, %, &, |, ^, ==, !=, >, <, >=, and <=.


The rules of binary promotion are as follows in the order specifed. First the compiler checks
if any of the operands is of type decimal. If it is, the other operand is now converted to a
decimal. But if the other operand happens to be foat or a double, an error results. This
concludes that we cannot have a binary operation performed on a decimal and a foat or
double. C# does not like the above potent combination. If you cast, all is forgiven.

The mystery as to why C# frst checks for decimal operands is yet not resolved. If none of the
operands is a decimal, the compiler checks with it can now see a double. If it does, it
converts the other to a double. If none of the parameters are a double it checks if one of
them is a foat. If afrmative, then it gets converted to a foat. This completes the numbers
243884994.doc 122 od 276
with decimal places. It then checks if the other parameter is a ulong. If yes, the other
parameter is converted to a ulong, if and only if it is not a sbyte, short, int or long. If it is we
get an error as usual. Then it checks for a long and converts the other operand to a long. No
complications as in the case of a ulong. The compiler also checks for a uint as one
parameter and the other a sbyte, short or int. If true, then hold your horses as both
parameters are converted to a long and not a uint.

Whatever happened to rationality and consistency? Why did change only for a uint. If one of
them is a uint and the other not a decimal, foat, short etc, then we follow the old golden
rules and both are converted to a uint. This means a uint and byte in a binary operator, the
byte becomes a uint. Finally, if none apply, both parameters become an int. This applies not
to user defned operators but in expressions.

To reiterate some of the restrictions; we cannot mix decimal with double and foat types. A
complete no-no as there are no implicit conversions defned between these type. We cannot
mix and match a ulong with any signed integral type. The reason being there exists no third
data type that can represent the range of a ulong and a signed data type at the same time.
This explains the caveats mentioned earlier for a ulong.



a.cs
public class zzz
{
public static void Main()
{
double i 0.,;
deci'al ; 0..;
%&ste'.(onsole.)rite#ine(i$;);
}
}

(o'piler +rror
a.cs(5-0,)/ error (%!55>/ #iteral of t&pe double cannot be i'plicitl& converted to t&pe 3deci'al3;
use an 3M3 suffi4 to create a literal of this t&pe
a.cs(1-.5)/ error (%!!06/ *perator 3$3 cannot be applied to operands of t&pe 3double3 and 3deci'al3

By default a number with a decimal place is a double. We cannot equate a double to a
decimal or add a decimal and a double as discussed earlier.

a.cs
public class zzz {
public static void Main() {
double i 0.,;
deci'al ; 0..';
%&ste'.(onsole.)rite#ine(i$(double););
%&ste'.(onsole.)rite#ine((deci'al)i$;);
}
}

*utput
..8
..8

A number ending with m becomes a decimal number. Also, we can cast a decimal to a
double and vice versa and the compiler shows thumbs up. Casting can accomplish anything
243884994.doc 126 od 276
you can dream up. It can convert raw dirt into gold only if you learn to say please the right
way.
a.cs
class zzz
{
public static int i'plicit operator (zzz f)
{
}
}

(o'piler +rror
a.cs(,-08)/ error (%088,/ Ceclaration is not valid; use 3i'plicit operator "destOt&pe> (...3 instead

)he com!ier oves thin"s 'ein" !aced in the ri"ht order. :or a conversion o!erator+ the order
is access modifier+ then static+ foo#ed 'y im!icit or e$!icit and then the name of the
o!erator to convert to. )his order cannot 'e chan"ed under any circumstance. ;ay 'e it<s
times for the com!ier to "et off its hi"h horse+ #ot=
Chapter #

$perators

We all like to express ourselves in diferent ways. Some smile, some laugh, some screech and
some even shout. The C# programming language lays a lot of emphasis on expressions and
has one of the largest chapters in its documentation devoted to it. An expression is a
sequence of operators and operands providing some computational activity.

An expression can be built in n number of ways. It can be as simple as a value resulting in a
certain type. It can also consist of a variable with a type. A namespace can also occur in an
expression but it must be only at the beginning. A type like a namespace can also occur on
the left hand side of an expression. The usual gang of idiots like properties, indexers,
properties can also be included in an expression. However, the fnal result of an expression
can never ever be a namespace, type, method group or event access. These can only occur in
intermediate results.

Expressions are not conjured out of thin air but are made up of only two entities, operators
and what we ofer them in homage, operands. Operands can include constants, variables,
indexers etc. The second entity, operators is what this chapter is dedicated to.


)hecked and 'nchecked perators

a.cs
class zzz
{
b&te i .88;
b&te ; .85;
public static void Main()
{
b&te A O0;
}
}

(o'piler +rror
243884994.doc 127 od 276
a.cs(1-0!)/ error (%!!,0/ (onstant value 3O03 cannot be converted to a 3b&te3
a.cs(>-0!)/ error (%!!,0/ (onstant value 3.853 cannot be converted to a 3b&te3

A byte can only store values from 0 to 255. We have initialized i to the largest possible value
a byte can store i.e. 255. For the second variable, we try and go beyond the range that a byte
can handle. Well, the compiler comes back screaming at us with an error. This is the
behaviour in the case of variable j. This is enough proof that the compiler checks the values,
the variables are being initialized to. For that matter, it could be a local variable created in a
function or an instance variable created out of a function. Come what may, C# does not
allow you to take a byte and give it a value falling outside its range of 0 to 255.

a.cs
class zzz
{
public static void Main()
{
b&te A (b&te).85;
b&te l (b&te).81;
b&te ' (b&te)80>;
%&ste'.(onsole.)rite#ine(A $ 9 9 $ l $ 9 9 $ ');
}
}
(o'piler +rror
a.cs(8-00)/ error (%!..0/ (onstant value 3.853 cannot be converted to a 3b&te3 (use 3unchecAed3
s&nta4 to override)
a.cs(5-00)/ error (%!..0/ (onstant value 3.813 cannot be converted to a 3b&te3 (use 3unchecAed3
s&nta4 to override)
a.cs(1-00)/ error (%!..0/ (onstant value 380>3 cannot be converted to a 3b&te3 (use 3unchecAed3
s&nta4 to override)

However, one way out is by using a cast. A cast is some data type within () brackets. The
object on the right temporarily is converted to the data type within the cast. It is not like an
operation where you get a permanent scar. This conversion is forgotten when you leave the
statement. No permanent damage or change is carried out on the object.

By casting, we are commanding the compiler to break or overlook its own rules. C# realizes
that 256 is larger than 255, so it will keep subtracting the number from 256. The resultant
value is the number assigned to the byte variables.

To place it more accurately, the compiler divides the number by 256 and the remainder is
the assigned value to the variable. Pure mathematics in school revealed that when any
number is divided by another number lets say 100, the remainder is always less than 100
(the divisor). This remainder is now a byte that can be equated to the byte variables on the
left.

The error demands an unchecked modifer while casting.

a.cs
class zzz
{
public static void Main()
{
b&te A unchecAed((b&te).85);
b&te l unchecAed((b&te).81);
b&te ' unchecAed((b&te)80>);
%&ste'.(onsole.)rite#ine(A $ 9 9 $ l $ 9 9 $ ');
}
243884994.doc 128 od 276
}

*utput
! 0 .

a.cs
class zzz {
int b 0!!!!!!;
int c 0!!!!!!;
public static void Main()
{
int ;;
zzz a new zzz();
; a.abc(a.b-a.c);
%&ste'.(onsole.)rite#ine(;);
; a.p<r(a.b-a.c);
%&ste'.(onsole.)rite#ine(;);
a.4&z(a.b-a.c);
}
int abc( int 4- int &)
{
return 4W&;
}
int p<r( int 4- int &)
{
return unchecAed(4W&);
}
int 4&z( int 4- int &)
{
return checAed(4W&);
}
}

*utput
O1.1,16657
O1.1,16657

+4ception occurred/ %&ste'.*verflow+4ception/ :n e4ception of t&pe %&ste'.*verflow+4ception
was thrown.
at zzz.Main()

There are two operators in the C# programming language called checked and unchecked.
They deal with problems of overfow or underfow in values. In the above examples, the
compiler is aware of the problem at compile time. A vast majority of us believe that
computers are highly intelligent. The same people also believe in the tooth fairy. What we
would like to say very emphatically is that the compilers do not go far enough in
understanding what our program is up to. They apply a simple set of rules and if our
program fails them, an error is fagged.

By default, C# writes the operator unchecked before all our expressions. Thus the functions
abc and pqr behave in the same way.

The compiler adds a lot of its code to the code we write as well as rewrites a major part of it
too. In the function abc or pqr, the compiler does not actually replace the variables with
their values while compiling. If it did so, it would come back and inform us about an
overfow. The unchecked operator behaves like our parents. They normally let us do what we
want as they believe we are old enough to decide our fate. In this case the compiler does
243884994.doc 129 od 276
assume you know what you are doing and simply gives you the wrong answer of overfow
results.

Remember the compiler does not hand run your code. If we use the checked operator, the
compiler will now behave in a diferent way at run time but not at compile time. It will now
generate an exception if an overfow occurs. Thus, it keeps a watchful eye on your program
at runtime. It is advisable to use the checked operator, as we do not know what values our
variables may hold. Also, please catch the exception thrown to prevent the error message
box showing up as it will scare the shits of the user.

a.cs
class zzz
{
const int 4 0!!!!!!;
const int & 0!!!!!!;
static int abc() {
return checAed(4 W &);
}
static int p<r() {
return unchecAed(4 W &);
}
static int 4&z() {
return 4 W &;
}
int aaa() {
return 4 W &;
}
static void Main()
{
}
}

(o'piler +rror
a.cs(5-05)/ error (%!..!/ =he operation overflows at co'pile ti'e in checAed 'ode
a.cs(0.-7)/ error (%!..!/ =he operation overflows at co'pile ti'e in checAed 'ode
a.cs(08-7)/ error (%!..!/ =he operation overflows at co'pile ti'e in checAed 'ode

C# treats a const variable very diferently from a normal one. In the case of a const, the
compiler is cent percent sure that the value will never change.

Now C# hits the ceiling as in the function abc, it detects an overfow. It gives an error about
the impending danger. In the case of function pqr, using unchecked, we have informed the
compiler not to bother us as we are old enough to understand what we are doing; even
though it is blatantly obvious that we are not in this case. C# bows to our wishes and issues
no error message.

Under normal circumstances, when one of these operators is not specifed for compile time
checks, by default, the reverse of it is applicable for the run time checks. At compile time,
the checked operator is on for const variables by default, irrespective of the modifer static.
This explains the last two errors. Thus for compile time checks the default is checked for
const variables and the compiler will try as hard as possible to check for overfows. However,
it will never replace variables with values.



a.cs
243884994.doc 133 od 276
class zzz
{
public const int i checAed((int).0>1>7,5>7);
public const int ; unchecAed((int).0>1>7,5>7);
}

(o'piler +rror
a.cs(,-,0)/ error (%!..0/ (onstant value 3.0>1>7,5>73 cannot be converted to a 3int3 (use
3unchecAed3 s&nta4 to override)

a.cs
class zzz
{
public int A unchecAed((int).0>1>7,5>7);
public int l (int).0>1>7,5>7;
public int ' checAed((int).0>1>7,5>7);
}

(o'piler +rror
a.cs(>-01)/ error (%!..0/ (onstant value 3.0>1>7,5>73 cannot be converted to a 3int3 (use
3unchecAed3 s&nta4 to override)
a.cs(8-.8)/ error (%!..0/ (onstant value 3.0>1>7,5>73 cannot be converted to a 3int3 (use
3unchecAed3 s&nta4 to override)

The only diference between the two programs is that in the frst program, the variables are
const whereas in the second program they are not. The error messages are the same. We
could not combine them into one program as at times the compiler stops at the frst error
message it encounters. Sometimes it does not display all of them.

We are trying to cast a number larger than an int to an int. Thus we see the error. Reduce
the number by one and the error like a nightmare disappears. This happens only for
checked and not unchecked.

Remember for compile time checks the default is checked in case you have become
absentminded along the way. The ++, -- operators act on overfow in the same way.

a.cs
class zzz
{
public static void Main()
{
int A 588,1;
short ; (short)A;
%&ste'.(onsole.)rite#ine(;);
short l checAed((short)A);
%&ste'.(onsole.)rite#ine(l);
}
}

*utput
0
+4ception occurred/ %&ste'.*verflow+4ception/ :n e4ception of t&pe %&ste'.*verflow+4ception
was thrown.
at zzz.Main()

The same rule also applies to conversions. We cannot directly convert an int to a short as
the short is smaller in range of values than an int. Hence the cast. The compiler is oblivious
of the error we have made and when we run the program, the short j gets truncated. This is
243884994.doc 131 od 276
so because the number gets divided by 65536, but as the default is unchecked no exception
is thrown. In the second case, however, we have explicitly used the operator checked.

Remember, the rules for a constant and non-constant expression are diferent. Run time
behaviour can be changed by compile time switches. What we've learnt is the default
behaviour as of this version. The behaviour can either be checked or unchecked but there is
no in between option. The checked and unchecked operators are not nosy parkers. They
restrict themselves only to the () brackets. Their domain does not extend beyond that.

$s operator

a.cs
class &&&
{
}
class zzz
{
public static void Main()
{
&&& a new &&&();
if ( a is &&&)
%&ste'.(onsole.)rite#ine(9&&&9);
if ( a is %&ste'.%tring)
%&ste'.(onsole.)rite#ine(9string9);
if ( a is ob;ect)
%&ste'.(onsole.)rite#ine(9ob;ect9);
}
}

(o'piler )arning
a.cs(6-5)/ warning (%!07,/ =he given e4pression is alwa&s of the provided (3&&&3) t&pe
a.cs(00-5)/ warning (%!07>/ =he given e4pression is never of the provided (3string3) t&pe
a.cs(0,-5)/ warning (%!07,/ =he given e4pression is alwa&s of the provided (3ob;ect3) t&pe

*utput
&&&
ob;ect

The 'is' operator is used to determine the run time type of an object or its compatibility with
another type. It returns either a true or false, that is, a boolean. As the object a is an
instance of class yyy, 'a is yyy' is obviously true. Also, a not being a string will not execute
the second System.Writeln. Finally, as all classes derive from object the last if results in a
true hence object is displayed.

The warnings are issued as even a blind man will tell you that the conditions in the if
statement are determinable at run time. Anything that the compiler can't fgure out and can
be determined at run time is fagged as a warning.

a.cs
class &&&
{
}
class zzz
{
public static void Main()
{
if ( &&& is a)
%&ste'.(onsole.)rite#ine(9&&&9);
243884994.doc 132 od 276
}
}

(o'piler +rror
a.cs(7-5)/ error (%!007/ 3&&&3 denotes a 3class3 where a 3variable3 was e4pected

The operands to the 'is' operator cannot be reversed. It frst requires the name of the object
and then the class or type name.

a.cs
class &&&
{
}
class 444
{
}
class zzz
{
public static void Main()
{
zzz a new zzz();
&&& b new &&&();
444 c new 444();
a.abc(b);
a.abc(c);
a.abc(.!);
a.abc(9hi9);
}
public void abc(ob;ect o)
{
if ( o is &&&)
%&ste'.(onsole.)rite#ine(9&&& 9 $ o);
if ( o is 444 )
%&ste'.(onsole.)rite#ine(9&&& 9 $ o);
if ( o is string )
%&ste'.(onsole.)rite#ine(9&&& 9 $ o);
}
}

*utput
&&& &&&
&&& 444
&&& hi

The compiler gives us no warnings. Whenever we call a function, we need to be very
particular about the data type of the parameter or else it will generate an error. The frst
exception to this rule can be when objects are cast to each other. The second exception can
be with objects that are derived from each other. As all classes derive from the Object class,
the parameter set in function abc can be of object type, which means that abc can now
receive diferent parameter types. It will not throw any error.

We have three is operators checking whether o is a yyy, xxx or string. The WriteLine function
also displays the object. Remember we are checking o with diferent class names and the if
statement will be true only once in each case.

a.cs
class &&&
{
}
243884994.doc 133 od 276
class 444
{
}
class zzz
{
public static void Main()
{
zzz a new zzz();
444 c new 444();
if (c is &&&)
%&ste'.(onsole.)rite#ine(9&&& 9 $ c);
}
}
(o'piler )arning
a.cs(0,-8)/ warning (%!07>/ =he given e4pression is never of the provided (3&&&3) t&pe

In the above case, all that we receive is a warning. The object c is an instance of xxx and not
yyy. There is no way for the compiler to convert a xxx object into a yyy object. The warning
illustrates the wisdom of the compiler.

a.cs
class &&&
{
}
class 444
{
public static e4plicit operator &&& (444 a)
{
return new &&&();
}
}
class zzz
{
public static void Main()
{
444 c new 444();
if (c is &&&)
%&ste'.(onsole.)rite#ine(9&&& 9 $ c);
else
%&ste'.(onsole.)rite#ine(9false 9 $ c);
}
}

(o'piler )arning
a.cs(01-8)/ warning (%!07>/ =he given e4pression is never of the provided (3&&&3) t&pe

*utput
false 444

In this program, we thought the explicit operator introduced would convert c, an xxx object
to yyy. We pleaded hard enough but it just did not help. We replaced the explicit modifer
with implicit but the compiler as adamant it is, refused to convert the xxx object into a yyy
object. We should have read the warning more carefully.

a.cs
class &&&
{
}
class 444
{
243884994.doc 134 od 276
public static i'plicit operator &&& (444 a)
{
%&ste'.(onsole.)rite#ine(9operator9);
return new &&&();
}
}
class zzz
{
public static void Main() {
zzz a new zzz();
444 c new 444();
a.abc(c);
}
public void abc(&&& o)
{
if (o is &&&)
%&ste'.(onsole.)rite#ine(9&&& 9 $ o);
}
}

(o'piler )arning
a.cs(.0-8)/ warning (%!07,/ =he given e4pression is alwa&s of the provided (3&&&3) t&pe

*utput
operator
&&& &&&

We now reverted to our earlier program where we passed the object as a type yyy class, yet
the compiler gives us the same old warning. The compiler can convert an xxx to a yyy
thanks to the operator. If we change the parameter in abc from class yyy to object, the is will
still be false.
The return value of an is operator notifes whether the object can be converted to a
particular data type or not. The datatype follows the is operator whereas the object precedes
it. The is operator ignores user defned conversions and only looks at reference conversions.
It can be used with value and reference types.

The as operator

a.cs
class zzz
{
public static void Main()
{
string s;
s 9hi9 as string;
%&ste'.(onsole.)rite#ine(s);
s 0!! as string;
%&ste'.(onsole.)rite#ine(s);
}
}

(o'piler +rror
a.cs(7-5)/ error (%!!,6/ (annot convert t&pe 3int3 to 3string3 via a builtOin conversion

The as operator behaves like a cast. It converts one data type into another. Thus in the frst
case, it converts "hi" into a string. As hi can be converted to a string, the value of s is hi.
However, a 100 cannot be converted into a string and thus we get an error. A cast normally
throws an exception whereas the as operator returns a error.
243884994.doc 132 od 276

a.cs
class zzz {
public static void Main()
{
int i 0!! as int;
}
}


(o'piler +rror
a.cs(>-6)/ error (%!!11/ =he as operator 'ust be used with a reference t&pe (3int3 is a value t&pe)

The error message for once makes it very clear that the as operator does not accept a value
type like int but requires reference types like string. The earlier rules on user supplied
conversions et all apply verbatim here also.

The )onditional perator

a.cs
class zzz {
public static void Main() {
int i 0!;
string ;;
; i > .! G 9hi9 / 9b&e9;
%&ste'.(onsole.)rite#ine(;);
; i > . G 9hi9 / 9b&e9;
%&ste'.(onsole.)rite#ine(;);
}
}

*utput
b&e
hi

The conditional operator is also called the ternary operator as it takes three operands. The
frst is the condition to be checked. If it results in true then the answer lies between the ?
and :. If it evaluates to false, the answer is within the : to the semi colon. Thus the ? :
operator operates like an if else on one line. As the C programming language ofered us this
operator and C++ followed suit, then how could C# refuse. It is available but use it at your
own risk.

a.cs
class zzz {
public static void Main() {
int i 0!;
string ;;
; i > .! G 1 > , G 9no9 / 9b&e9 / 9&es9 ;
%&ste'.(onsole.)rite#ine(;);
; i > .! G 1 " , G 9no9 / 9b&e9 / 9&es9 ;
%&ste'.(onsole.)rite#ine(;);
; i " .! G 1 " , G 9no9 / 9b&e9 / 9&es9 ;
%&ste'.(onsole.)rite#ine(;);
; i " .! G 1 > , G 9no9 / 9b&e9 / 9&es9 ;
%&ste'.(onsole.)rite#ine(;);
}
}

*utput
243884994.doc 136 od 276
&es
&es
b&e
no

The ?: operator is right associative, which means that it gets evaluated from right to left. In
the frst case, the compiler only sees the condition i >= 20. As it is false everything in
between the ? : is ignored and the string j contains yes. In the second case, the same rule
applies and the compiler misses the fact that we have changed something from the ? to
colon. In the third case, the condition is true, the compiler proceeds to the ? onwards and
here sees another conditional operator. As it evaluates to false, the result is bye and in the
last case it evaluates to true and thus the result is no.

The type of the conditional operator depends on the type of its two operands. In the above
case, the type is string as both the operands are strings.

a.cs
class zzz
{
public static void Main()
{
ob;ect i;
444 4 new 444();
&&& & new &&&();
i 1 > , G 4 / & ;
%&ste'.(onsole.)rite#ine(i);
}
}
class 444
{
}
class &&&
{
}

(o'piler +rror
a.cs(7-08)/ error (%!01,/ =&pe of conditional e4pression can3t be deter'ined because there is no
i'plicit conversion between 34443 and 3&&&3

The compiler tries to convert an xxx type object to a yyy type object but there is no such
implicit conversion available.

a.cs
class zzz
{
public static void Main()
{
ob;ect i;
444 4 new 444();
&&& & new &&&();
i 1 > , G 4 / & ;
%&ste'.(onsole.)rite#ine(i);
}
}
class 444
{
public static i'plicit operator &&& ( 444 a)
{
%&ste'.(onsole.)rite#ine(9operator9);
243884994.doc 137 od 276
return new &&&();
}
}
class &&&
{
}


*utput
operator
&&&

We do not receive any error as the compiler converts the xxx object to a yyy object using the
implicit operator supplied. The implicit operator performs its job by converting the xxx to
another data type, in this case yyy. Finally, the ToString function of the object i is called
which displays its data type as yyy.

a.cs
class zzz
{
public static void Main()
{
ob;ect i;
444 4 new 444();
&&& & new &&&();
i 1 " , G 4 / & ;
%&ste'.(onsole.)rite#ine(i);
}
}
class 444
{
}
class &&&
{
public static i'plicit operator 444 ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator9);
return new 444();
}
}

*utput
operator
444

We have brought about two changes. First, we've changed the condition to return false so
that the result now is a yyy object. Next is that the implicit operator is used to convert the
data type to xxx. So much done, the compiler now wants to determine the controlling type of
the conditional operator. It tries to convert one to another and if it fails, it fags an error. It
tries the type conversion from the 2nd operand to 3rd and then vice versa.

i 1 > , G 4 / & ;

If we reverse the logical condition, the resultant output now shows:

*utput
444

243884994.doc 138 od 276
The compiler has a mind of its own. It realizes that the logical condition is true and the
result is x, an xxx object. As it is, i is already an xxx object and the conversion available is
more to convert from yyy to xxx, so it doesn't use it. Hence, the operator is not called. When
the condition turns out to be false, as seen earlier, the result is a yyy object. This needs
conversion so we bring in a user defned operator to do it.

a.cs
class zzz
{
public static void Main()
{
ob;ect i;
444 4 new 444();
&&& & new &&&();
i 1 " , G 4 / & ;
%&ste'.(onsole.)rite#ine(i);
}
}
class 444
{
public static i'plicit operator &&& ( 444 a)
{
%&ste'.(onsole.)rite#ine(9operator &&&9);
return new &&&();
}
}
class &&&
{
public static i'plicit operator 444 ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator 4449);
return new 444();
}
}

(o'piler +rror
a.cs(7-05)/ error (%!01./ =&pe of conditional e4pression can3t be deter'ined because 34443 and
3&&&3 both i'plicitl& convert to each other

The problems of plenty! We have supplied an operator to convert a yyy object to an xxx
object and vice versa. This confuses the life out of the compiler and therefore we get an error
as both operators can do the same job. If the compiler chooses any one of them, it could be
accused of being impartial. Nor does it like gambling. So it fags an error.

a.cs
class zzz
{
public static void Main()
{
444 i;
444 4 new 444();
&&& & new &&&();
int a >- b 5;
i a > b G 4 / & ;
%&ste'.(onsole.)rite#ine(i);
}
}
class 444 {
public static i'plicit operator &&& ( 444 a)
{
243884994.doc 139 od 276
%&ste'.(onsole.)rite#ine(9operator9);
return new &&&();
}
}
class &&& {
}
(o'piler +rror
a.cs(6-5)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3&&&3 to 34443

The above program tours you into the mind of a compiler. It gives you a good insight into its
workings. The conditional operator has a condition whose result can be estimated only at
run time. Thus the compiler cannot fathom whether the type of the operator will be xxx or
yyy. The compiler assumes the result to be a yyy object. Under this pretext, the following
result of the conditional too has to be stored in a xxx type object. However, there is no such
conversion available hence it errs. The compiler could have assumed the conditional to be
true thereby resulting in an xxx object. Then no conversions would be required. Well, it
performs both these checks, hence it fails just to be on the side of your safety.

If we change the data type of i from an xxx to yyy, we get no errors as the compiler assumes
that the result of the operator will be a xxx as the worst case possible. It has an operator to
convert to a yyy. Move the operator function to the yyy class and the error will now read as
given below.

(o'piler +rror
a.cs(6-5)/ error (%!!.6/ (annot i'plicitl& convert t&pe 34443 to 3&&&3

Let us now fnd out how C# evaluates a conditional operator.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
int i a G 0! / .! ;
%&ste'.(onsole.)rite#ine(i);
}
}
class &&&
{
}
(o'piler +rror
a.cs(5-00)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3&&&3 to 3bool3

The compiler expects a condition expression with a ternary operator. It tries to convert a yyy
object into a bool and realizes that there is no such possibility. So it throws up its hands in
despair and gives us the above error.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
int i a G 0! / .! ;
%&ste'.(onsole.)rite#ine(i);
}
}
243884994.doc 143 od 276
class &&&
{
public static bool operator true ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator true9);
return true;
}
public static bool operator false ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator false9);
return true;
}
}

*utput
operator true
0!

We create an operator called true, which the compiler calls to fgure out the value of the
logical expression. As we returned true in our operator, the conditional operator is
considered as true and hence the result is 10.
The operator true is ideal for conditions where we supply an object in place of a condition
and require a boolean. This operator converts a yyy to a boolean true . Replace the return
true in operator true to return false. The output will be operator true and 20.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
int i a G 0! / .! ;
%&ste'.(onsole.)rite#ine(i);
}
}
class &&&
{
public static i'plicit operator bool( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator bool9);
return true;
}
public static bool operator true ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator true9);
return true;
}
public static bool operator false ( &&& a)
{
%&ste'.(onsole.)rite#ine(9operator false9);
return true;
}
}

*utput
operator bool
0!

We've learnt from the experts, that is, C#, on how to complicate lives. To convert a yyy object
to a boolean value, it frst looks for the operator bool. This operator returns a true or false
243884994.doc 141 od 276
depending upon the parameter given to it. As we are explaining concepts, we return true. If
the operator bool is present, like in our case, C# will not look further for an operator true. If
it is not available, like in the earlier example, then the operator true gets called. Remember
the order is frst bool then true. If both are present, fortunately, we get no error.

So what happens to operator false? If both of these are not available, will it take operator
false? Well, this is not the case. We land up with an error as shown below.

(o'piler +rror
a.cs(0.-.!)/ error (%!.05/ =he operator 3&&&.operator false(&&&)3 re<uires a 'atching operator
3true3 to also be defined

Try giving only operator true and it will shoot up a similar error.

&oolean perators

The if statement, until this moment, is the only known way to make our programs highly
intelligent. To add some more intelligence to the if statement within our code, we use the
boolean operators. They are the & (and) and | (or). These operators make the if statement
more restrictive. They also add more conditions or intelligence to the if statement. The more
complex the if statement, the more dynamic our code becomes.

The & sign is a short form for and whereas the | sign stands for the or.

a.cs
class zzz
{
public static void Main()
{
int i 0- ; .;
if ( i > 0 &; > 0)
%&ste'.(onsole.)rite#ine(9&true9);
if ( i > 0 &; " 0)
%&ste'.(onsole.)rite#ine(9&false9);
if ( i > 0 V ; " 0 )
%&ste'.(onsole.)rite#ine(9V true9);
if ( i > 0! V ; > 0)
%&ste'.(onsole.)rite#ine(9V again true9);
}
}

*utput
&true
V true
V again true

The & in the if make the if true when both conditions are true. As i is greater than equal to
1 and j is greater than 1, the frst if is true. Regardless of the frst condition being true, the
second if evaluates to false because the second condition is false. The | is true if any one of
the conditions is true. In the last two if's, even though one of the conditions is true, the
expression evaluates to true.

a.cs
class zzz {
public static void Main() {
int i 0- ; .;
243884994.doc 142 od 276
if ( i > 0 &&; > 0)
%&ste'.(onsole.)rite#ine(9&&true9);
if ( i > 0 &&; " 0)
%&ste'.(onsole.)rite#ine(9&&false9);
if ( i > 0 VV ; " 0 )
%&ste'.(onsole.)rite#ine(9VV true9);
if ( i > 0! VV ; > 0)
%&ste'.(onsole.)rite#ine(9VV again true9);
}
}

*utput
&&true
VV true
VV again true

We have simply replaced the & with && and ditto for the |. Yet, there seems to be no
perceivable diference between them.
Lets us now write a set of programs to understand the diference.

a.cs
class zzz
{
public static void Main()
{
int i 0- ; .; int A !; int l !;
if ( i > $$A &; > $$l)
%&ste'.(onsole.)rite#ine(9&true 9 $ 9 9 $ A $ 9 9 $ l);
if ( i > $$A &; " $$l) ;
%&ste'.(onsole.)rite#ine(9&false 9 $ 9 9 $ A $ 9 9 $ l);
if ( i > $$A V ; " l$$ ) ;
%&ste'.(onsole.)rite#ine(9V true 9 $ 9 9 $ A $ 9 9 $ l);
A O0;
if ( i > $$A V ; > l$$)
%&ste'.(onsole.)rite#ine(9V again true 9 $ 9 9 $ A $ 9 9 $ l);
}
}

*utput
&true 0 0
&false . .
V true , ,
V again true ! >

a.cs
class zzz {
public static void Main()
{
int i 0- ; .; int A !; int l !;
if (i > $$A &&; > $$l)
%&ste'.(onsole.)rite#ine(9&&true 9 $ 9 9 $ A $ 9 9 $ l);
if (i > $$A &&; " $$l) ;
%&ste'.(onsole.)rite#ine(9&&false 9 $ 9 9 $ A $ 9 9 $ l);
if (i > $$A VV ; " l$$) ;
%&ste'.(onsole.)rite#ine(9VV true 9 $ 9 9 $ A $ 9 9 $ l);
A O0;
if ( i > $$A VV ; > l$$)
%&ste'.(onsole.)rite#ine(9VV again true 9 $ 9 9 $ A $ 9 9 $ l);
}
243884994.doc 143 od 276
}
*utput
&&true 0 0
&&false . 0
VV true , .
VV again true ! .

Two identical programs except the diference between the single and double logical operators
and you see two completely diferent outputs.

In the frst program, C# simply increments the variable l each time it increments k. A single
&, after evaluating the frst condition, proceeds to the second condition and increments l.
Hence we see k and l with the value of 1and 1 for each of them.

Let's take a similar case in the second program. The condition in the if statement reads (i >=
++k && j > ++l). The value of k which is zero frst becomes one. The value of i is one and
hence the frst condition is true as '1 is >= 1'. C# now goes to the second condition as if this
condition is false, the entire expression evaluates to false. It now increments l to 1 and as j
is 2, (2>1), thus making the condition true.

The second if statement in the second program reads (i >= ++k && j <= ++l). At frst k grows
from 1 to 2 whereas i remains at 1, hence the condition is false. C# now realizes that it is
fruitless evaluating the second condition as evaluating it to true or false will not afect the if
result of false anymore. It ignores the second condition, therefore l retains its old value of 1.

&& are called short circuit logical operators. A single & will always execute both the
conditions and show an increase in the value of l unlike && which distances itself from the
second condition on failure of the frst.

The single | works in a slightly similar way. When the frst condition of the or is true, C#
considers it to be pointless in evaluating the remaining conditions as the or will remain true
irrespective of the result of the second condition. In the condition (i >= ++k || j < l++),
variable i still holds 1 and variable k increases to 3. As the frst condition is false, C#
executes the second as its result can efect the overall result of the compound condition.

In the last case, the frst condition is true, so no more of the if conditions are read. The
variable l restores its original value.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine( true Y false);
%&ste'.(onsole.)rite#ine( false Y true);
%&ste'.(onsole.)rite#ine( false Y false);
%&ste'.(onsole.)rite#ine( true Y true);
}
}

*utput
=rue
=rue
Ialse
Ialse
243884994.doc 144 od 276

The ^ operator works in a similar way to the | operator with one subtle change. If both sides
are true, the answer is not true but false. The rest of the conditions work like an or. This is
called the logical xor operator. More on this issue a couple of pages later.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine( true U false);
%&ste'.(onsole.)rite#ine( false U true);
%&ste'.(onsole.)rite#ine( false U false);
%&ste'.(onsole.)rite#ine( true U true);
}
}

*utput
=rue
=rue
Ialse
Ialse

If you observe carefully, the != operator and the ^ operator produce the same result. Let the
time of the day decide on the option you choose for a logical comparison. Many a times, like
in life, you wonder why C# programming language has certain language features, which are
redundant and can duplicated by another set of commands.

a.cs
class zzz
{
public static void Main() {
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine( a &&b);
}
}
class &&&
{
public static bool operator &(&&& 4-&&& &)
{
%&ste'.(onsole.)rite#ine(9op9);
return true;
}
}

(o'piler +rror
a.cs(5-.1)/ error (%!.01/ @n order to be applicable as a short circuit operator a userOdefined
logical operator (3&&&.operator &(&&&- &&&)3) 'ust have the sa'e return t&pe as the t&pe of its .
para'eters.

The same error keeps popping up. A large number of operators demand their return type to
be the class they are created in. At times, we understand why and at other times, we feel we
are not intelligent enough to fathom a higher intellect.
a.cs
class zzz
{
public static void Main()
{
243884994.doc 142 od 276
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine( a &&b);
}
}
class &&&
{
public static &&& operator &(&&& 4-&&& &)
{
%&ste'.(onsole.)rite#ine(9op9);
return new &&&();
}
}

(o'piler +rror
a.cs(1-.1)/ error (%!.07/ =he t&pe (3&&&3) 'ust contain declarations of operator true and operator
false

We do as ordered by the error message and bring in an operator true and an operator false.
These are needed as our answer belongs to the boolean world. To evaluate the condition in
the WriteLine function, we need the above true and false operators. Along with it, we also
include the implicit string operator as System.Console.WriteLine takes a string.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&(0);
&&& b new &&&(.);
%&ste'.(onsole.)rite#ine( a &&b);
%&ste'.(onsole.)rite#ine( a &b);
}
}
class &&&
{
int ;;
public &&& ( int v)
{
; v;
}
public static &&& operator &(&&& 4-&&& &)
{
%&ste'.(onsole.)rite#ine(9op 9 $ 4.; $ 9 9 $ &.;);
return new &&&(4.;$&.;);
}
public static bool operator true(&&& 4)
{
%&ste'.(onsole.)rite#ine(9true 9 $ 4.;);
return true;
}
public static bool operator false(&&& 4)
{
%&ste'.(onsole.)rite#ine(9false 9 $ 4.;);
return true;
}
public static i'plicit operator string (&&& 4)
{
%&ste'.(onsole.)rite#ine(9string 9 $ 4.;);
return 9&&& 9 $ 4.;;
}
243884994.doc 146 od 276
}

*utput
false 0
string 0
&&& 0
op 0 .
string ,
&&& ,

We were astonished with the results. Operator & is not called ever. We introduced two new
operators for it and & operator itself never gets called. So, we removed the code from our
class yyy. When we do so, we get an error.


(o'piler +rror
a.cs(1-.1)/ error (%!!06/ *perator 3&&3 cannot be applied to operands of t&pe 3&&&3 and 3&&&3

If we want it to understand the yyy object, we need to overload the && operator. After a
while, we realized that our user defned function only implements one & while we had used
&& in the WriteLine function. We tried to implement the && by adding one more & to our
operator. On doing so, we get the following error.

(o'piler +rror
a.cs(0.-.7)/ error (%0!.!/ *verloadable binar& operator e4pected

Thus we cannot overload the &&. The user-defned operator & handles both the & and the
&& but with a subtle diference between them. The expression a && b is evaluated as
T.false(a) ? a : T.&(a,b). In the above case, object a is represented by the number 1 and the
object b by a number 2.

C# frst calls the operator false using object a with j value as 1. As we are returning true, the
answer is object a (from ? to : ) , the string operator is used to produce yyy 1.

For the non-short circuit operators, the & is called and life moves on in the fast track like
normal as we are returning an object comprised of the sums of objects a and b. This is
converted into a string and its values are displayed.

Comment the lines for the & as it remains the same always.

BB%&ste'.(onsole.)rite#ine( a &b);

Now let the freworks start. Instead of returning true in the operator false, we now give false.

public static bool operator false(&&& 4)
{
%&ste'.(onsole.)rite#ine(9false 9 $ 4.;);
return false;
}
We now see the following result"

*utput
false 0
op 0 .
string ,
&&& ,
243884994.doc 147 od 276

As the operator false returns false, condition T.false(a) ? a : T.&(a,b) evaluates to false. This
calls the operator & with a and b, which creates a new object. Hence, we see op displayed.
The explanation for operator & holds true thereafter. The short circuit operator works on the
principle that if the operator false returns true, the entire statement is false and no more
code is executed.

For the or operator |, the rule is a || b and it gets evaluated to T.True(a) ? a : T.|(a,b). We
change the & to the | in the operator and also in the WriteLine functions. Make the
following changes to Main

public static void Main() {
&&& a new &&&(0);
&&& b new &&&(.);
%&ste'.(onsole.)rite#ine( a VV b);
%&ste'.(onsole.)rite#ine( a V b);
}

and in class yyy we've changed the & operator to |

public static &&& operator V (&&& 4-&&& &)
{
%&ste'.(onsole.)rite#ine(9op 9 $ 4.; $ 9 9 $ &.;);
return new &&&(4.;$&.;);
}

With the true operator returning true, we see the following output.

*utput
true 0
string 0
&&& 0
op 0 .
string ,
&&& ,

With the true operator returning false, the results are as follows:

*utput
true 0
op 0 .
string ,
&&& ,
op 0 .
string ,
&&& ,

The | works the same way as the &. There is no change at all. To understand the above
output, we need to jog our memory with the fact that a || will only proceed if it meets a
false condition at the beginning. If it meets a true condition, it blatantly ignores the
following condition as the return value of true or false would not afect the answer. Thus, as
in life, if we get what we want we do not push ourselves. We also learnt that a true or false
operator is used to fgure out the end result of a condition. Also, the object a is always
evaluated only once but the object b may or may never be evaluated. An expression to be
used as a logical expression must either have an operator bool or a true.

243884994.doc 148 od 276
Heavy stuf! time to shift our minds to something diferent and easier.

Shi+t perators

a.cs
class zzz
{
public static void Main()
{
int i-; 7;
i ; >> .;
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
i ; "" .;
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
}
}

*utput
. 7
,. 7

The right shift operator >> shifts all the bits to the right. Shifting bits to the right is the
same as dividing the value by 2. The bits, that fall of from the right end are replaced with
bits having a zero value from the left. The left shift operator works in the reverse way and
move bits towards the right, The bits that come in from the right are zero bits and the ones
that fall of from the left are forgotten. This is like multiplying the value by 2 for each shift
that we make.

a.cs
class zzz {
public static void Main()
{
&&& a new &&&(0!);
int i a >> .;
%&ste'.(onsole.)rite#ine(i);
}
}
class &&&
{
int ; ;
public &&&(int p)
{
; p;
}
public static int operator >> ( &&& a- int b)
{
%&ste'.(onsole.)rite#ine(a.;$ 9 9 $ b);
return a.; $ b;
}
}

*utput
0! .
0.
We are allowed to overload the >> or the << operators as we overload any other operator. The
constructor is called with one parameter. The value of this parameter is given to the local
variable j. The object is now right shifted by 2 and the result is stored in i.

243884994.doc 149 od 276
The right shifting passes two parameters in the overloaded operator, that is, the instance of
the object and the next is the number if bits to be shifted. As long as we return a data type
that is the return value of the operator, what we do in the function is entirely at our
discretion. This is the beauty of operator overloading. It's like life. We decide what to make of
it and it is nobody's business to interfere in it. For an int, the shift count is not what we
specify but count & 0x1f and for a long, count & 0x3f. Count is the number of bits to shift.
God only knows why the above restriction. If the shift count is zero, the answer does not
change. Also, these operators cannot cause an overfow and produce the same results in
checked and unchecked mode.

,, and -- operators

The increment operator ++ can be used in two contexts with a variable. Prefx which means
before and postfx which means after. The results are normally the same but difer in a very
subtle way.

a.cs
class zzz {
public static void Main()
{
int i-;;
; 0;
i $$;;
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
i ;$$;
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
}
}

*utput
. .
. ,
The ++ before a variable is called the prefx increment operator. Here, the variable j, which
has a value of 1, frst becomes 2 and then this value is stored in i. Thus, at the end of the
statement, both i and j share the same value.

In the case of the postfx increment operator, the ++ appears after the variable name.

i=++j, in simple language reads as, initialize i to the existing value of the variable j that is 2.
The minute you reach the end of the statement, increment the value of the variable j by 1.
While the statement is being executed, j has a value of 2, outside the statement j has a value
of 3.

There are a large number of cases where the postfx operator makes our programming life
easier. Can we not have an operator that makes the rest of life better?

a.cs
class zzz {
public static void Main() {
&&& a new &&&();
%&ste'.(onsole.)rite#ine($$a.i);
}
}
class &&&
{
public int i
243884994.doc 123 od 276
{
get {return 8;}
set {}
}
}

*utput
5

The postfx and prefx operators can also work with a property, provided the property has
both a get and set accessor. If we remove the set accessor from the above example we receive
the following error message.
(o'piler +rror
a.cs(5-.7)/ error (%!.!!/ 2ropert& or inde4er 3&&&.i3 cannot be assigned to OO it is read onl&

and if we remove the get accessor and only have the set accessor.

(o'piler +rror
a.cs(5-.7)/ error (%!08>/ =he propert& or inde4er 3&&&.i3 cannot be used in this conte4t because it
lacAs the get accessor

The above errors are obvious as a ++ operator with a variable, i.e. writing i++, is a short
form of writing i = i + 1.

In such a case, we frst need to read the value of a variable, therefore we need a get accessor
and then we change the value of the same variable. So we require a set accessor too. The
indexer also works in the same fashion where the compiler does not even bother to have a
separate error message for them. The error message for the property and indexer are merged
into one. A lazy programmer !!!.

The predefned data types that understand the ++ and -- are sbyte, byte, short, ushort, int,
uint, long, ulong, char, foat, double, decimal, and any other enum type. What works for a +
+ also works in the same way for a --.

a.cs
class zzz
{
public static void Main()
{
}
}
class &&&
{
public static int operator $$ (&&& a)
{
}
}


(o'piler +rror
a.cs(6-06)/ error (%!886/ =he para'eter and return t&pe for $$ or OO operator 'ust be the
containing t&pe

The ++ operator belongs to a class and hence must return an object that is an instance of
the same class. The ++ operator in our code must return a yyy and not an int.

a.cs
243884994.doc 121 od 276
class zzz
{
public static void Main()
{
&&& a new &&&(,);
int i;
i $$a;
%&ste'.(onsole.)rite#ine(i);
i a$$;
%&ste'.(onsole.)rite#ine(i);
}
}
class &&&
{
int ;;
public &&&( int p)
{
; p;
}
public static &&& operator $$ (&&& a)
{
&&& b a;
%&ste'.(onsole.)rite#ine(9$$ 9 $ a.;);
b.;$$;
return b;
}
public static i'plicit operator int (&&& a)
{
%&ste'.(onsole.)rite#ine(9int 9 $ a.;);
return a.;;
}
}

*utput
$$ ,
int >
>
$$ >
int 8
8

In the ++ operator of the class, we return an object b, which is an instance of class yyy. We
frst initialize b to the parameter passed and then display the value of j.
System.Console.WriteLine shows ++3

The next line increments the member j by one thereby storing the value of 4 in j and then
the object is returned. Since the value is stored in an int variable, the implicit int function
nows comes into picture. The int operator converts the yyy to an int by returning the value
of the member j. Within int, the display line will show 'int 4' and fnally the WriteLine in
Main will display 4.

However, in the second case of a++, the same thing happens. The identical operator gets
called and we get the same result. Our reasoning was that the value of the yyy object would
be 4 and not 5 as the ++ was used in the postfx context. But, there is no diference at all.

Thus, in the user defned operators, the ++ works the same whether it is used in a prefx or
a postfx context. We do not have two diferent operators and our operators do not know
which context they are being called in. There is only one operator being used but in two
dissimilar ways.
243884994.doc 122 od 276

!ogical Negation . perator

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(Utrue);
%&ste'.(onsole.)rite#ine(Ufalse);
}
}

*utput
Ialse
=rue

This operator only acts on a bool. It reverses the condition. A true becomes false and a false
becomes true.

a.cs
class zzz {
public static void Main()
{
&&& a new &&&();
int i Ua;
%&ste'.(onsole.)rite#ine(i);
}
}
class &&&
{
public static int operator U (&&& a)
{
return 0;
}
}

*utput
0

The negation operator can be overloaded only for objects. This object becomes a parameter.
The important thing here is that there is no rule stating the data type of the return value. In
the above case we are returning an int for all that C# cares. Common sense demands that it
should be an int but One case where C# should have enforced a sensible rule but does
not. May be we need to wait till the next version arrives.



)omplement operator

a.cs
class zzz {
public static void Main()
{
int i ,.157;
int ; Zi;
%&ste'.(onsole.)rite#ine(;);
}
243884994.doc 123 od 276
}

*utput
O,.156

The bitwise operator ~ negates the bits. The zeros become one and the ones becomes zeros.
This operator is used when you want to reverse the bits in a byte.

Arithmetic perators

a.cs
class zzz {
public static void Main() {
double i , W ..!;
int ; (int), W ..!;
%&ste'.(onsole.)rite#ine(. W (float),.!);
%&ste'.(onsole.)rite#ine(. W ,.!);
%&ste'.(onsole.)rite#ine(,.! W (double).);
}
}

(o'piler +rror
a.cs(>-0!)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3double3 to 3int3

The multiplication * operator can multiply a foat or a double with an int. The second line
has a number with decimal places i.e. a double multiplied with an integer. It does the
multiplication and the answer is obviously a double. The multiplication goes through but
the double value in no way can be held in an int. The fault, dear Brutus, lies not in the
stars, but in the internal conversion from a double into an int.

Let us understand more of the above conversions.

Every time we create a variable, C# allocates some memory for it. We can determine this
memory by using the sizeof operator in an unsafe context. The size of the basic data types
are as follows, double 8, foat 4, long 8, int 4, uint 4, char 2, byte 1.

a.cs
class zzz
{
public static void Main()
{
double i !; float ; !;
i ;;
; i;
int A !; long l !;
A l;
l A;
b&te ' !;
' A;
A ';
}
}

(o'piler +rror
a.cs(1-8)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3double3 to 3float3
a.cs(6-8)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3long3 to 3int3
a.cs(0.-8)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3int3 to 3b&te3

243884994.doc 124 od 276
C# has a very simple rule when it comes to equating objects belonging to the simple data
types. It does not like any waste. Hence, we can equate a larger data type to a smaller data
type. Here, larger and smaller refer to the amount of memory a data type is allocated at the
time of creation.
On equating a larger data type to a smaller one, there is no reason for loss of data as in the
memory, variable on the left is large enough to hold the value stored of the right.

int b&te

However in the reverse case, the variable on the right will theoretically store a larger value
than the left side variable. As the variable on the left can store a smaller value, there is a
possibility of loss of data.

b&teint

Thus an int can be equated to a byte but a byte cannot be equated to an int. The range of
values an int can store is by far larger than what a byte can handle.

a.cs
class zzz {
public static void Main() {
int i;
i 0!B!;
}
}

(o'piler +rror
a.cs(>-8)/ error (%!!.!/ Civision b& constant zero

Whenever any number is divided by zero, the answer, as taught to us in school, is infnity.
No computer can ever evaluate this expression as it holds no defnition. Thus, one of the
million error checks that a compiler reviews is divide by zero.

a.cs
class zzz {
public static void Main()
{
int i; int ;!;
i 0!B;;
}
}
*utput
+4ception occurred/ %&ste'.Civide?&Eero+4ception/ :tte'pted to divide b& zero.
at zzz.Main()

At times, compilers can be exasperating. We love calling them all sorts of names. Here we
have very clearly, in front of the world, initialized the variable j to zero. The compiler knows
about it but seems to turn a blind eye to it. For some reason, as we mentioned earlier, the
compiler ignores alll variable values until the program is executed. This now throws an
exception at run time and since we haven't caught it, we see the error.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(0!B,);
243884994.doc 122 od 276
%&ste'.(onsole.)rite#ine((double)0!B,);
%&ste'.(onsole.)rite#ine((double)(int)0!B,);
%&ste'.(onsole.)rite#ine(0!B,.!);
%&ste'.(onsole.)rite#ine(0!.!B,);
}
}

*utput
,
,.,,,,,,,,,,,,,,
,.,,,,,,,,,,,,,,
,.,,,,,,,,,,,,,,
,.,,,,,,,,,,,,,,

When we divide 10 by 3 the answer is a whole number as both the operands are numbers.
This is called integer division and the little that you are knowledgeable about mathematics
would tell you that the answer is 3.3333 up to a million 3s. To convince the compiler to
give you the right answer, we need to convert one of the numbers into a foating number i.e.
a number with decimal places. The frst way is by using a cast. The frst time we cast it to
double and in the second case, 10 is frst casted to an int and then again to a double. C#
only remembers the last cast. The other way out is to make one of the parameters a double,
it does not matter which one. Its division results in a foating point answer.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(06[8);
%&ste'.(onsole.)rite#ine(O06[8);
%&ste'.(onsole.)rite#ine(06[O8);
}
}

*utput
>
O>
>
A large number of times, we are interested in the remainder of a division and not the
answer. To accomplish that, we have been ofered the reminder operator %. 19 divided by 5
gives us an answer of 3 and a remainder 4. In the second case the answer is a negative
value while in the last case the remainder is again a positive 4. The result of x%y is x-
(x/y)*y. If we divide by zero we get an exception thrown. Also, this operator never produces
an overfow.

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(06[8.0);
}
}

*utput
,.1

243884994.doc 126 od 276
This book will at times compel you to read the documentation to reason out on answers like
above. We quote verbatim. If z is the result of x % y and is computed as x - n * y, where n is
the largest possible integer that is less than or equal to x / y. Try making sense out of it. The
next line also throws in some rule called IEEE 754.

a.cs
class zzz {
public static void Main()
{
&&& a new &&&();
%&ste'.(onsole.)rite#ine(9hi 9 $ .!!);
%&ste'.(onsole.)rite#ine(.!! $ 9 hi9);
%&ste'.(onsole.)rite#ine(9hi 9 $ a);
}
}
class &&&
{
}

*utput
hi .!!
.!! hi
hi &&&

The + operator is normally used to add two numbers but is also smart enough to join or
concatenate two string together. If it does not fnd two strings, but a number and a string in
any order, it frst converts the number to a string and then joins them . If we do not state a
number but an object like class yyy, it will frst call the ToString function of the object class.
This will return a string representation of the class, and then join the two strings.

What works for operator + also works for operator - The rules of enum apply here too.
Obviously, you cannot subtract two strings from each other.

Anding and ring

a.cs
class zzz
{
public static void Main()
{
%&ste'.(onsole.)rite#ine(.0 &08 );
%&ste'.(onsole.)rite#ine(.0 V 00 );
%&ste'.(onsole.)rite#ine(.0 Y 00 );
}
}

*utput
8
,0
,!

The single & is very diferent from the &&. It stands for a logical anding and specifes that
both sides must be true to get a true result. If even one side is false, the answer is false.
Similarly a single | is called bitwise oring. In bitwise anding, we compare individual bits and
not the entire byte. If the individual bits are both one i.e. true then the answer is one. Even
if one of them is zero, the answer is zero. In bitwise oring, even if one of the bits is one, the
243884994.doc 127 od 276
answer is a 1. The ^ sign is called Xoring which is similar to oring except that if both bits
are 1, or 0 , in a xor we get 0. If one of the bits is 0 and the other 1, then we see 1.

a.cs
class zzz {
public static void Main()
{
int i-;-A;
i .0; ; 00;
A i Y ;;
%&ste'.(onsole.)rite#ine(A);
%&ste'.(onsole.)rite#ine(A Y i);
%&ste'.(onsole.)rite#ine(A Y ;);
}
}

*utput
,!
00
.0
Why, on earth, would anyone want an xor operator? The above program gives you one such
reason. When we xor 21 with 11, we get a answer of 30. Now, we take this answer and xor
with 21. Surprisingly, our answer is 11, the value of j. May be a fuke. So we now xor k with
j and now we get 21 which is the value of i.

Thus, the value obtained on xoring, when xored with any of the original number will give the
other original number. Xoring is the cornerstone of all encryption. These operators do not
produce an overfow. The xoring with numbers is useful rather than logical conditions that
we learnt earlier. These operators work seamlessly with any enumeration that we create with
no extra efort on our part.

Equality operators

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
a b;
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
}
}
class &&&
{
}

*utput
Ialse
=rue
=rue
Ialse


243884994.doc 128 od 276
We are comparing two objects for equality. Both are an instance of a class yyy, which is
devoid of anything. As the two objects a and b have been created in two separate areas of
memory, they are represented by a diferent memory location hence they are not equal to
each other. Thus the frst WriteLine function displays False and as they are not equal the
second displays a True. A user defned == or != operator are covered in the next series of
programs.

When we equate a to b, both a and b now, store a similar number. This is the address of b in
memory. As this number is the same, from C# point of view, they represent the same
reference object and hence we see a result of True.

A reference object is diferent from a value because the reference object refers to or stores a
value. This indicates its presence in memory. Simultaneously, class yyy may have a hundred
variables with diferent values in objects a and b, nevertheless, if we wrote a = b, they would
be termed as equal reference objects. As we mentioned earlier, value objects are considered
equal depending upon the value of the objects.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
}
}
class &&&
{
public static bool operator ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(99);
return true;
}
public static bool operator U ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9U9);
return true;
}
}

*utput

=rue
U
=rue

We are allowed to overwrite most of the operators provided we follow some basic rules of the
game. At times we have to return a certain data type. In the above case, we must have both,
== and != unless we are looking for the error as shown below.

(o'piler +rror
a.cs(0,-.!)/ error (%!.05/ =he operator 3&&&.operator (&&&- &&&)3 re<uires a 'atching operator 3U
3 to also be defined

We now rewrite the != operator by making it return an int.

243884994.doc 129 od 276
public static int operator U ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9U9);
return 5;
}

On doing so, we get the following error.

(o'piler +rror
a.cs(0,-.!)/ error (%!.05/ =he operator 3&&&.operator (&&&- &&&)3 re<uires a 'atching operator 3U
3 to also be defined
a.cs(07-06)/ error (%!.05/ =he operator 3&&&.operator U(&&&- &&&)3 re<uires a 'atching operator
33 to also be defined

We really confused the compiler as it expected both the operators to return the same values.
Then it must tell us so, as we've been telling you very often that return values do not count
as method signatures. Confusion everywhere!

We change the == operator to returning an int, and as both return an int now, C# has no
objections at all. Here, there are no rules in the datatype of returned objects.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
a b;
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
}
}
class &&&
{
}
class 444 / &&&
{
}

*utput
Ialse
=rue
=rue
Ialse

The result is the same as above due to the fact that what works with the same class will
obviously work with derived classes also. a and b start with not being true and then on
equating them, since they refer to the same object in memory, the reverse holds true.

Two small points which may have skipped your mind:

an xxx object in memory comprises of a xxx and yyy
we cannot equate b to a.

a.cs
243884994.doc 163 od 276
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
}
}
class &&&
{
}
class 444
{
}

(o'piler +rror
a.cs(1-.1)/ error (%!!06/ *perator 33 cannot be applied to operands of t&pe 3&&&3 and 34443
a.cs(7-.1)/ error (%!!06/ *perator 3U3 cannot be applied to operands of t&pe 3&&&3 and 34443

We cannot equate any two objects from any two classes with each other. This gives us a
compile time error rather than a true or false. C# has not been enlightened on the rules of
comparing a yyy and a xxx object for equality, hence the error.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
}
}
class &&&
{
public static bool operator ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(99);
return true;
}
public static bool operator U ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9U9);
return true;
}
}
class 444
{
}

*utput

=rue
U
=rue

243884994.doc 161 od 276
To eliminate the above error, we overload a set of == and != operators in the class yyy. The
second parameter to the operator must be changed to an xxx object as the default or free ==
operator takes two similar objects and checks for equality.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( b U a);
}
}
class &&&
{
public static bool operator ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9&&& 9);
return true;
}
public static bool operator U ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9&&& U9);
return true;
}
}
class 444
{
public static bool operator ( 444 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9444 9);
return true;
}
public static bool operator U ( 444 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9444 U9);
return true;
}
}

*utput
&&&
=rue
444 U
=rue

In this program, we have introduced the same code in class xxx and now we get no errors.
There is only one diference and that is the change in order of parameters. Here the object
xxx is placed as the frst parameter and not second. Thus in the == as the yyy object is frst
and then the xxx object, the == gets called from the yyy class. For the != operator, the
parameters type are reversed and as xxx object comes frst, the != operator gets called from
xxx instead.

We now rewrite only class xxx as follows and also change the WriteLine function back to a !=
b.

class 444
243884994.doc 162 od 276
{
public static bool operator ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9444 9);
return true;
}
public static bool operator U ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9444 U9);
return true;
}
}

(o'piler +rror
a.cs(1-.1)/ error (%!0.0/ =he call is a'biguous between the following 'ethods or properties/
3444.operator (&&&- 444)3 and 3&&&.operator (&&&- 444)3
a.cs(7-.1)/ error (%!!06/ *perator 3U3 cannot be applied to operands of t&pe 34443 and 3&&&3

The error appears as C# is all at sea and cannot decide which == operator to call. At a == b,
it can call both, the one from xxx as well as the one from yyy. When we have two equally
probable options, C# does not randomly chose one of them but throws up its hands in
exasperation and gives us an error.

a.cs
class zzz {
public static void Main()
{
&&& a new &&&();
444 b new 444();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( b U a);
}
}
class &&&
{
public static bool operator ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9&&& 9);
return true;
}
public static bool operator U ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9&&& U9);
return true;
}
}
class 444
{
}

(o'piler +rror
a.cs(7-.1)/ error (%!!06/ *perator 3U3 cannot be applied to operands of t&pe 34443 and 3&&&3

Method signatures are extremely important. The != can only compare an object of type yyy
and xxx. The yyy object comes frst as the function signature specifes it frst. In our case,
the xxx object comes frst and thus the error. At certain places, especially function
parameters, we cannot change the order of parameters. One option is to have a == with both
orders of objects.

a.cs
243884994.doc 163 od 276
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( b U a);
}
}
class &&&
{
public static bool operator ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9@n class &&& 9);
return true;
}
public static bool operator ( 444 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9@n class &&& 09);
return true;
}
public static bool operator U ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9@n class &&& U9);
return true;
}
public static bool operator U ( 444 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9@n class &&& U 09);
return true;
}
}
class 444
{
}

*utput
@n class &&&
=rue
@n class &&& U 0
=rue

A rather long program for no fault of ours. We frst place a != operator and reverse the
parameters in System.Console.WriteLine. Now we overload the == and the != operators but
with diferent ordering of the objects, so the method signature changes. These two newly
introduced functions are placed in class yyy. Remember, whenever any entity comes in
pairs, the data type of the parameter list is important.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( b U a);
}
}
class &&&
243884994.doc 164 od 276
{
public static bool operator ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9&&& 9);
return true;
}
public static bool operator U ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9&&& U9);
return true;
}
}
class 444
{
public static i'plicit operator &&&(444 a)
{
%&ste'.(onsole.)rite#ine(9operator &&&9);
return new &&&();
}
}

*utput
operator &&&
&&&
=rue
operator &&&
&&& U
=rue

Is there something wrong somewhere ?. a == b does generate any error as b, an xxx object,
can be converted to a yyy object using the operator yyy in class xxx. The operator yyy has
only one job to do and that is to convert an xxx object into a yyy object.

We are highly impressed with the compiler as it has a lot of foresight. It would realize that
the == cannot accept an xxx as the frst parameter. It can only accept a yyy. There is,
however, some light at the end of the tunnel as there is a possibility of converting an xxx to
a yyy object. Once done, the == operator is called with two yyy objects as parameters. Ditto
for the !=. Smart guy.
a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( 9OOOO9);
%&ste'.(onsole.)rite#ine( b U a);
}
}
class &&&
{
public static bool operator ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9&&& 9);
return true;
}
public static bool operator U ( &&& 4- 444 z)
{
%&ste'.(onsole.)rite#ine(9&&& U9);
return true;
243884994.doc 162 od 276
}
public static i'plicit operator 444(&&& a)
{
%&ste'.(onsole.)rite#ine(9operator 444 9);
return new 444();
}
}
class 444
{
public static i'plicit operator &&&(444 a)
{
%&ste'.(onsole.)rite#ine(9operator &&&9);
return new &&&();
}
}

*utput
&&&
=rue
OOOO
operator &&&
operator 444
&&& U
=rue

C# goes to great lengths not to give you any error. The == operator has no problems at all. It
has the right parameters and the right data types also. For the !=, we have lots of trouble.
The != operator in yyy requires the frst parameter to be a yyy and the second to be an xxx
whereas the expression to be evaluated has an xxx frst and then a yyy. C# realizes that it
can convert an xxx object to a yyy object using the operator yyy in class xxx. This has been
explained earlier. So the frst parameter to the != meets its match. The second one is a
problem as it is a yyy whereas it should have been an xxx. C# wears its thinking cap and
realizes that the class yyy has an operator xxx which it can use to convert the yyy object to
an xxx, thus satisfying the parameter data type for !=.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
aaa c new aaa();
%&ste'.(onsole.)rite#ine( c b);
%&ste'.(onsole.)rite#ine( b U a);
}
}
class &&&
{
public static i'plicit operator aaa(&&& a)
{
%&ste'.(onsole.)rite#ine(9&&& aaa9);
return new aaa();
}
}
class 444
{
public static i'plicit operator aaa(444 a)
{
%&ste'.(onsole.)rite#ine(9444 aaa9);
243884994.doc 166 od 276
return new aaa();
}
}
class aaa
{
public static bool operator ( aaa 4- aaa z)
{
%&ste'.(onsole.)rite#ine(9aaa 9);
return true;
}
public static bool operator U ( aaa 4- aaa z)
{
%&ste'.(onsole.)rite#ine(9aaa U9);
return true;
}
}

(o'piler +rror
a.cs(6-.1)/ error (%!!06/ *perator 3U3 cannot be applied to operands of t&pe 34443 and 3&&&3

This version of C# will never enter the Mensa club as it may be smart but not a genius. We
expected a lot from the C# compiler. What we thought it would do is convert both yyy and
xxx into aaa objects using the operator aaa functions from each class. Now that it has two
aaa objects, it could use them and call the != from class aaa. Unfortunately, it did go far
enough, but not very far.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
}
}
struct &&&
{
}

(o'piler +rror
a.cs(1-.1)/ error (%!!06/ *perator 33 cannot be applied to operands of t&pe 3&&&3 and 3&&&3
a.cs(7-.1)/ error (%!!06/ *perator 3U3 cannot be applied to operands of t&pe 3&&&3 and 3&&&3

Hey, remember we told you a long time ago that structures were value types. Only reference
types have the pleasure of getting a free == and != operators not value types.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine( a b);
%&ste'.(onsole.)rite#ine( a U b);
}
}
struct &&&
243884994.doc 167 od 276
{
public static bool operator ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9&&& 9);
return true;
}
public static bool operator U ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9&&& U9);
return true;
}
}

*utput
&&&
=rue
&&& U
=rue

Thus the only way out here is, to do as earlier, create our own != and == operators. All the
above rules hold good here too.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine( (ob;ect)a b);
%&ste'.(onsole.)rite#ine( a U (ob;ect)b);
}
}
class &&&
{
public static bool operator ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9&&& 9);
return true;
}
public static bool operator U ( &&& 4- &&& z)
{
%&ste'.(onsole.)rite#ine(9&&& U9);
return true;
}
}

*utput
Ialse
=rue

Our operators are not called at all! The reason being that if we cast any one of the
parameters to an object, then the == and != operators from the object class are called. We
call the == signs as the reference type equality operator. We could comfortably remove the
code of the == and != operators from class yyy and still get no errors. This proves that they
are not needed in the frst place.

a.cs
class zzz
{
243884994.doc 168 od 276
public static void Main()
{
string s 9zzz9;
string t string.(op&(s);
%&ste'.(onsole.)rite#ine(s t);
%&ste'.(onsole.)rite#ine((ob;ect)s t);
%&ste'.(onsole.)rite#ine(s (ob;ect)t);
%&ste'.(onsole.)rite#ine((ob;ect)s (ob;ect)t);
}
}

*utput
=rue
Ialse
Ialse
Ialse

The example above is brazenly stolen from the documentation without any compulsions. S is
a string initialized to zzz. t, also a string, is initialized using the Copy command to a newly
allocated area in memory storing zzz..

We have two zzz strings in memory in diferent locations. The == operator of the string class
does not look at the values of the objects but when it peeks into their diferent memory
locations, it realizes that the strings are the same even though the objects are diferent. As a
result, it returns true.

On the next line, we cast one of the parameters to an object. The original == operator applies
its own set of rules where it compares actual references and not what is stored. This results
in the last three WriteLine functions giving a false. Had we simply equated a to t as in s = t,
all the WriteLine functions would result in true as the strings are equal in value and what
they represent.

a.cs
class zzz
{
public static void Main()
{
int s 0!;
int t 0!;
%&ste'.(onsole.)rite#ine(s t);
BB%&ste'.(onsole.)rite#ine((ob;ect)s t);
%&ste'.(onsole.)rite#ine((ob;ect)s (ob;ect)t);
}
}

*utput
=rue
Ialse

When we equate two value objects, the == operator checks for actual value of the object. As s
and t have the same value of 10, they are considered to be at par. If, however, we cast one of
them to an object, an operation called boxing takes place.

In a case such as this, we are actually creating a reference object that looks like object in a
certain area of memory. Thus, the s is created in one part of memory as an object. Ditto for
t. Now that t resides in a separate area of memory, inspite of having the same value, as
243884994.doc 169 od 276
reference objects they are considered unequal. Therefore, the System.Console. WriteLine
function displays false.

If we remove the above comment, the compiler fings an error.

(o'piler +rror
a.cs(7-.1)/ error (%!!06/ *perator 33 cannot be applied to operands of t&pe 3ob;ect3 and 3int3

The == operator has overloads that work with ints or objects but as such there is no
overload that takes an object and an int. The only rational reason available to us at this
moment is that it makes no sense in equating a value object to a reference object. Impressive
enough, C# is the frst language known to us to make a profound distinction between value
type variables and reference type variables.

#elegate equality

Man has always fought for equality all his life. Wars have been won or lost for the principles
of equality. What if we write some pages on equality of delegates.

a.cs
public class zzz
{
public static void Main()
{
aa a new aa();
a.abc();
}
}
public class aa
{
public delegate void 4&z();
public delegate void p<r();
void p<r0 ()
{
}
void 4&z0()
{
}
public void abc()
{
p<r d new p<r(p<r0);
p<r e new p<r(p<r0);
%&ste'.(onsole.)rite#ine( d e);
4&z f new 4&z(4&z0);
%&ste'.(onsole.)rite#ine( d f);
4&z g new 4&z(p<r0);
%&ste'.(onsole.)rite#ine( d g);
%&ste'.(onsole.)rite#ine( f g);
}
}

*utput
=rue
Ialse
=rue
Ialse

243884994.doc 173 od 276
Delegates are considered equal if the reference is made to the same delegate type. Even
though, delegates d and e are created with a separate new i.e. they are stored in diferent
parts of memory, from the viewpoint of delegates, they are treated as equal. Delegate f is of
xyz type but more important while it was being instantiated we used xyz1, a diferent
function from class aa and hence they are diferent delegates. Delegate g is equal to d as the
same function pqr1 is used at the time of instantiating. The irony of the matter is that even
though f an g are technically of the same type, they are not equal as they represent diferent
functions. The function signatures are more important than delegate types. Thus, we have
seen a situation where delegates of diferent types are equal but the same types are unequal.

Assignment

The assignment operator = can only have three types of entities on its left or better said as
its left operand. These are: a variable, a property and a indexer. The last two must have a set
accessor defned if they are to be used.

a.cs
public class zzz
{
public static void Main()
{
1 6;
}
}

(o'piler +rror
a.cs(8-0)/ error (%!0,0/ =he leftOhand side of an assign'ent 'ust be a variable- propert& or
inde4er

If we are still unsure about the left operand of a assignment operator, the above error
message removes all doubts form our minds. The = is called the simple assignment operator.
a.cs
public class zzz
{
public static void Main()
{
int i-;-A;
A 0!;
i ; A;
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ; $ 9 9 $ A);
}
}

*utput
0! 0! 0!

We can write multiple = on the same line. This assignment is right-associative. In plain
English language it means, read from right to left or even better, backwards. First, the
compiler only sees j = k. As the value of k is 10, it initializes j to 10. Then i becomes 10, the
value of j. The above is simple case of convenience. i=j=k can also be read as i=(j=k).

The right operand to the equal to must be converted to the left operand type or else await a
casting error. You must have seen this error n number of times in the past. The = operation
simply assigns the value of the right operand to the left and is classifed as a value.

a.cs
243884994.doc 171 od 276
public class zzz {
public static void Main()
{
&&& a new &&&(0!-.!);
&&& b;
b a;
%&ste'.(onsole.)rite#ine( b.i $ 9 9 $ b.;);
}
}
class &&&
{
public int i-;;
public &&&(int 4- int &)
{
%&ste'.(onsole.)rite#ine(9(onst9);
i4;;&;
}
}

*utput
(onst
0! .!

As we have created only one object that looks like yyy, the constructor gets called only once.
Also, there is no code of the overloaded = operator in class yyy. Yet, we do not get any error
and also the assignment works as advertised. This is due to a free assignment operator from
C#.

Lately, we have been procuring a lot of free goodies from the compiler. This free operator
simply copies all the values of the variables from the object to the right to the object to the
left. Thus, the variables i and j within the object b store the values of i and j variables from
a.

We have not created object b to look like yyy and if you do remember what we learnt earlier,
b now stores the address of a. Had we instantiated an object that looks like yyy, the efect
would have been identical.

a.cs
public class zzz
{
public static void Main()
{
}
}
class &&&
{
public int i-;;
public &&&(int 4- int &)
{
%&ste'.(onsole.)rite#ine(9(onst9);
i4;;&;
}
public static &&& operator ( &&& a)
{
return new &&&(0-.);
}
}

(o'piler +rror
243884994.doc 172 od 276
a.cs(08-.7)/ error (%0!06/ *verloadable unar& operator e4pected

Error messages should always be loud and clear rather than beat around the bush. C#
should be telling us that come what may, there is one operator that no man can ever
overload and that is the assignment operator. The above meek error message is saying just
the same, something we forcefully said earlier. Thou ain't allowed to overload the assignment
operator under any circumstance!
a.cs
public class zzz
{
public static void Main()
{
string [] s new string[,];
ob;ect [] t s;
t[!] null;
t[0] 9hi9;
t[.] new &&&();
}
}
class &&&
{
}

*utput
Hnhandled +4ception/%&ste'.:rra&=&peMis'atch+4ception/ :n e4ception of t&pe
%&ste'.:rra&=&peMis'atch+4ception was thrown.
at zzz.Main()

The compiler gives no errors whatsoever. However, when we try to run our program, an
exception gets forcefully thrown at us. An array object is allowed to be a reference to another
array object.
Remember all objects are fnally derived from object, the base class. Even though, t is an
array of objects, it now co-exists with an array of strings. This is because we have initialized
t to s. Initializing object t[0] to null is permitted for reference types only. t[1]="hi" gives us no
error as a string is derived form object. Ditto for yyy. At compile time, we get no errors at all.
This rule is called the array co-variance rule.

When we run the program, an exception is thrown. One of the reasons is that at runtime, a
check is performed on the possibility of converting a yyy object to a string. The data type of
the array is string. As it fails, an exception is thrown. Remember t may be an array of
objects, but s is an array of strings and at the end of the day, the rules binding s apply. This
check is carried out at run time only. Why, only C# knows?!

a.cs
public class zzz
{
public static void Main()
{
ob;ect [] s new ob;ect[,];
string [] t s;
}
}

(o'piler +rror
a.cs(5-08)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3ob;ect[]3 to 3string[]3

The other way round generates a compiler error.
243884994.doc 173 od 276

a.cs
public class zzz {
public static void Main()
{
string [] s new string[,];
ob;ect [] t s;
t[!] (string)new &&&();
%&ste'.(onsole.)rite#ine(t[!]);
t[0] new &&&();
%&ste'.(onsole.)rite#ine(t[0]);
}
}
class &&&
{
public static i'plicit operator string ( &&& a)
{
return 9hi9;
}
}

*utput
hi
Hnhandled +4ception/%&ste'.:rra&=&peMis'atch+4ception/ +4ception of t&pe
%&ste'.:rra&=&peMis'atch+4ception was thrown.
at zzz.Main()

We tried to act oversmart. We added an implicit operator string to convert a yyy into a string.
We expected C# to realize at run time, that there is an operator string in class yyy which it
can use to prevent an exception being thrown. By using this operator it can convert a yyy
into a string. False hopes as C# ignores the operator completely.

In the earlier statement, we've explicitly type casted yyy to a string thereby enforcing the
compiler to use the string operator. At times we have to be explicit otherwise no one listens
to us. Being subtle does not help at all.

a.cs
public class zzz {
public static void Main() {
&&& a new &&&(0);
a.4 .;
%&ste'.(onsole.)rite#ine(9OOO9 $ a.4);
444 b new 444();
b.r new &&&(0!);
b.r.p 8!;
%&ste'.(onsole.)rite#ine(9OOO9 $ b.r.p);
b.r.4 0!!;
%&ste'.(onsole.)rite#ine(9OOO9 $ b.r.4);
}
}
class &&&
{
public int p;
public &&&(int i)
{
p i;
}
public int 4
{
get
243884994.doc 174 od 276
{
%&ste'.(onsole.)rite#ine(9&&& get 9 $ p);
return p;
}
set
{
%&ste'.(onsole.)rite#ine(9&&& set 9 $ value);
p value;
}
}
}
struct 444
{
&&& <;
public &&& r
{
get
{
%&ste'.(onsole.)rite#ine(9444 get 9 $ <.p $ 9 9 $ <.4);
return <;
}
set
{
< value;
%&ste'.(onsole.)rite#ine(9444 set 9 $ <.p $ 9 9 $ <.4 $ 9 9 $ value);
}
}
}
*utput
&&& set .
&&& get .
OOO.
&&& get 0!
444 set 0! 0! &&&
&&& get 0!
444 get 0! 0!
&&& get 8!
444 get 8! 8!
OOO8!
&&& get 8!
444 get 8! 8!
&&& set 0!!
&&& get 0!!
444 get 0!! 0!!
&&& get 0!!
OOO0!!

In the above example, yyy is a class with an instance variable called p and a property called
x. At the time of creation, the p variable in object a is initialized to 1. Then, using the set
property in x, it is reinitialized to 2. Thus, the WriteLine function displays 2.

The structure xxx contains a variable q that looks like yyy and a property r that returns a
yyy. We frst, create an object that looks like xxx and store it in b. Then we initialize the
property r to an object that looks like yyy. Within the set of the property, we initialize the
object q to this new yyy object. Thus, the variable p of object q will now be 10.

When we write b.r.p=50 or b.r.x=100, we are frst calling the get of the property r. This will
return a value object that looks like yyy and from this object we are changing p or x as the
case may be to 50 or 100. These objects are all stack-based hence the original remains the
same. Remember, with a structure we can only get a value, it is not the actual location of
243884994.doc 172 od 276
the variable and therefore any changes made will never change the values in the original
variable. As a result, the left operand must always be the name of a variable if we ever need
to see its value change.

A compound assignment is the assignment operator with a tweeze of lemon.

a.cs
public class zzz
{
public static void Main()
{
int i .;
int ; 0!;
; $ i;
%&ste'.(onsole.)rite#ine(;);
}
}

*utput
0.

There are a million ways to skin a cat and there are a million ways to initialize a variable. We
learnt of two ways earlier and this is the third way. Why waste your time and mine with one
more way in performing the same thing. Most of the operators behave in the same way as a
+ does. So the minus operator has an equivalent -= avatar. Thus i += j becomes i = i + j
bearing in mind that i is evaluated only once.

a.cs
public class zzz
{
int [] a {0-.-,};
public static void Main()
{
zzz z new zzz();
z.4&z()[z.p<r()] $ z.abc();
%&ste'.(onsole.)rite#ine(z.a[0]);
}
public int abc()
{
%&ste'.(onsole.)rite#ine(9abc9);
return 0!;
}
public int p<r()
{
%&ste'.(onsole.)rite#ine(9p<r9);
return 0;
}
public int [] 4&z()
{
%&ste'.(onsole.)rite#ine(94&z9);
return a;
}
}

*utput
4&z
p<r
abc
0.

243884994.doc 176 od 276
Mindblowing example! When we see a statement as in z.xyz()[z.pqr()] += z.abc(), we are sure
to be all lost. The compiler maintains its sanity and starts from the beginning of the line.
First, it simply executes a function called xyz. This function returns an array a which has
three members; the second element a[1] has a value of two. Thus the compiler substitutes
z.xyz() with a, in this specifc case. Now it needs the index of the array. Here it sees one more
function pqr. It executes this function and reverts with a 1. Hence the statement now reads
as a[1] += z.abc(). As function abc returns 10, the expression reads as a[1]+=10. The value in
a[1] is increased by 10 thereby making it 12.

Break up the code and see only the parts C# sees and the confusion will disappear from
your mind. Wherever statements like x = x + y are used, they can be replaced with x += y. All
the earlier rules of the simple assignment apply to the compound assignment also.

perator *erloading

When it starts on, the C# compiler overloads most of the operators to understand the inbuilt
data types. We have almost to a hundred of operator overloads already available with us. We
can create our own and as general principle, C# gives our user defned operators a frst shot
and then tries its in built operators. If none of the above work, it then fags us an error.
These user defned operator overloads works in the same manner for a class and a struct.
We are allowed to overload only eight unary operators, which are +, -, !, ~, ++, --,true and
false. However, when it comes to the binary oeprators we are ofered more variety. Here we
are allowed 15 of them, namely +, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >= and fnally the
<=.

For reasons unknown to us, we cannot overload all the operators. The ones beyond our
bounds are member access, method invocation (), =, &&, ||, ?:, new, typeof, sizeof, and
fnally the is operator. There is normally method behind madness and in this case for some
these overloads there exists a reasonable reason why the compiler forbids the overloads.

a.cs
public class zzz {
public static void Main()
{
&&& a new &&&(0);
&&& b new &&&(.);
&&& c new &&&(,);;
c $ a;
%&ste'.(onsole.)rite#ine(a $ b);
%&ste'.(onsole.)rite#ine(c);
}
}
class &&&
{
int i;
public &&&(int ;)
{
i ;;
}
public static &&& operator $ ( &&& a - &&& b)
{
%&ste'.(onsole.)rite#ine(9operator $ 9 $ a.i $ 9 9 $ b.i);
return new &&&(a.i$b.i);
}
public static i'plicit operator string(&&& a)
{
243884994.doc 177 od 276
return 9&&& 9 $ a.i;
}
}

*utput
operator $ , 0
operator $ 0 .
&&& ,
&&& >

Here, the class yyy is diferentiated from another instance of it by means of the instance
variable i. If a class only had functions, there would be no way to distinguish one instance
from another. The constructor initializes the variable i to a value of 1 and 2 respectively. We
have overloaded the + operator. This operator automatically overloads the += as a statement
c += a fnally becomes c = c + a. Even if we wanted to, the compiler would give an error, as
the + operator doubles up for the += also. This explains the frst WriteLine function where
the value of i in object c is 3.

In the operator plus, we are creating a new object whose variable i is the summation of the
i's of the objects a and b. The normal plus operator works as usual. We normally overrule
the predefned string operator that always gets called whenever our user defned type has to
be converted to a string. This is needed to get a confrmation that the plus operator actually
did the job it was supposed to do.

a.cs
public class zzz {
public static void Main()
{
}
}
class &&& {
public static &&& operator $ ( 444 a- 444 b)
{
return new &&&();
}
public static &&& operator $$ ( 444 a)
{
return new &&&();
}
}
class 444
{
}

(o'piler +rror
a.cs(1-06)/ error (%!85,/ *ne of the para'eters of a binar& operator 'ust be the containing t&pe
a.cs(00-06)/ error (%!886/ =he para'eter and return t&pe for $$ or OO operator 'ust be the
containing t&pe

Whenever an operator is overloaded, one of the parameters to the function must be the data
type of the class that contains the operator. In this case, while overloading the binary
operator +, at the most one of its parameters must be of type yyy. If not, then it is assumed
to be redefning an operator of another class. In the above case, the code of the operator +
belongs to class xxx and not yyy.

243884994.doc 178 od 276
In the case of a unary operator, it is evident that the only parameter must be the type of the
class. In case you have forgotten, the only ternary operator cannot be overloaded. By failing
the above rules, you can never supersede an existing predefned operator defnition.

The cast operators are resolved using the same principles but by using type names instead
of operator symbols. Element access like an array is achieved using indexers that has
absolutely has no relationship at all with operator overloads.

We do not have the leeway of doing whatever we want with an operator. We cannot change
the precedence or the associativity of the operator under any other circumstance. By
overwriting the plus operator, there is no way we can specify what its precedence now
should be.

The compiler does not enforce any rules of sanity when we write code for an operator
overload. Thus, even if common sense tells us that the logical operator == should always
return a boolean value, it is not mandatory to do so. If you return, say, an int instead, C#
turns a blind eye to it. It is your fduciary responsibility as an adult to return appropriate
values and write the right logical code in your operator functions. Do not look to the
compiler ever for help in catching logical errors. In this century at least!

a.cs
public class zzz {
public static void Main()
{
444 a new 444();
444 b new 444();
%&ste'.(*nsole.)rite#ine(a$b);
}
}
class &&&
{
public static &&& operator $ ( &&& a- &&& b)
{
return new &&&();
}
}
class 444
{
public static i'plicit operator aaa ( 444 a)
{
return new aaa();
}
}
class aaa
{
public static i'plicit operator &&& ( aaa a)
{
return new &&&();
}
}

(o'piler +rror
a.cs(5-.5)/ error (%!!06/ *perator 3$3 cannot be applied to operands of t&pe 34443 and 34443

Call it a limitation or a lack of foresight on the part of the compiler. We are trying to add two
objects that look like xxx. The compiler goes to the class and tries to fnd an operator that
can add two xxx objects. It does not fnd one and tries various permutations and
243884994.doc 179 od 276
combinations all within the class xxx only. As it does not succeed, it tries all of its
predefned + operators. None succeed and hence we get an error.

What we expected of the compiler was to have realized that there was a way to convert a xxx
object into an aaa object. On converting, it should have looked into the aaa class and
converted the aaa object into a yyy object using the respective operators. Then it should
have called the overloaded + operator of the class yyy and got the job done.

The point here is that C# is so one-track that it keeps trying in the same class and does not
bother to look into another class at all. The programming language C++ on which C# is
based would stand on its head to prevent an error from happening. C# is not as
adventurous as we expected it to be. You let us down Pal.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&(0);
&&& b new &&&(.);
%&ste'.(onsole.)rite#ine(a$b);
}
}
class &&&
{
int i;
public &&&(int ;)
{
i ;;
}
public static &&& operator $ ( &&& a- 444 b)
{
%&ste'.(onsole.)rite#ine(9op $ 9 $ a.i $ 9 9 $ b.;);
return new &&&(a.i$b.;);
}
public static i'plicit operator 444(&&& a)
{
%&ste'.(onsole.)rite#ine(9op 444 9 $ a.i);
return new 444(a.i);
}
public static i'plicit operator string( &&& a)
{
return 9&&& 9 $ a.i;
}
}
class 444
{
public int ;;
public 444(int A)
{
; A;
}
}

*utput
op 444 .
op $ 0 .
&&& ,

243884994.doc 183 od 276
As mentioned earlier, C# tries a little to prevent an error from happening but it does not try
hard enough. We are trying to add two yyy objects. However, in the class yyy, we have an
overloaded plus operator that accepts a yyy and a xxx object. Thus, the compiler now
realizes that it can convert a yyy object into a xxx object using the operator xxx. It converts
the second parameter b to a xxx object and now calls the operator plus with the right data
types. For the beneft of the WriteLine function, it will convert the resulting yyy object into a
string. If the string operator was not overloaded, it would use the ToString function of the
object class and simply display yyy. There would be no way to verify on the value of i, which
is 3.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&(0);
&&& b new &&&(.);
%&ste'.(onsole.)rite#ine(a$b);
}
}
class &&&
{
int i;
public &&&(int ;)
{
i ;;
}
public static i'plicit operator string( &&& a)
{
return 9&&& 9 $ a.i;
}
}

*utput
&&& 0&&& .

Hey, it works! No errors at all. How come? The compiler realized that there was no overload
of the + operator that accepts two yyy objects in class yyy. So, it acted smart and converted
both of them into a string. It calls the string operator of yyy twice to convert both objects a
and b to a string and then concatenates them together. How's that for an intelligent
compiler!

Remember, it does not peek into another class not try any sort of permutations and
combinations. It will only look in the current class and frst try and use the user defned
plus before trying the predefned plus operator. We have confrmation of the above statement
as in the earlier program, it did not use the predefned + operator but our user defned
operator. If we remove the operator xxx from class yyy from the above program + 1, the
string operator would be called. This is because the compiler cannot use the user defned +
operator as there exists no conversion between a yyy to a xxx object. Remember C# uses his
own operators as a last resort only.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&(0);
243884994.doc 181 od 276
&&& b new &&&(.);
%&ste'.(onsole.)rite#ine(a$b);
}
}
class &&&
{
int i;
public &&&(int ;)
{
i ;;
}
public static i'plicit operator string( &&& a)
{
return 9&&& 9 $ a.i;
}
public static i'plicit operator int ( &&& a)
{
return a.i;
}
}

(o'piler +rror
a.cs(1-.5)/ error (%!!,>/ *perator 3$3 is a'biguous on operands of t&pe 3&&&3 and 3&&&3


It is very difcult to confuse the compiler. We have added a way for the compiler to convert a
yyy object to an int. Thus we have created a problem for ourselves. It is equally probable for
C# to convert the two yyy objects into strings and then add them up or convert them to
numbers and do the same. Equally probable, thus C# does not like to play favorites and
gives us an error.

An operator defnition or declaration must include the modifers public and static. The
compiler generates an error if you do not specify one. You have to specify both. These are
like death and taxes. You cannot escape any of them. Maybe it would be a better idea for the
compiler not to mandate on modifers. Why always write the obvious.

a.cs
class &&&
{
public static &&& operator $ (&&& a- ref int i)
{
return new &&&();
}
}

An operator can only accept parameters passed as value and not passed as value or ref. Had
the designers of C# favoured, they could have easily removed the above restriction. Once
again, we come across a design decision made by the designers thus conveying to follow
their dictates with a smile on our faces.

The signature of an operator does not include the return type like a function. We cannot
have two operators having the same parameter list but difering only in return types. If you
recall, we had the same rule for overloading functions in a class. Consistency, thy name is
C#.

a.cs
class &&&
{
243884994.doc 182 od 276
public static &&& operator $ (&&& a- &&& b)
{
return new &&&();
}
public static &&& opQ:ddition (&&& a- &&& b)
{
return new &&&();
}
}

(o'piler +rror
a.cs(1-06)/ error (%!000/ (lass 3&&&3 alread& defines a 'e'ber called 3opQ:ddition3 with the sa'e
para'eter t&pes

Every time we create an operator function, internally the compiler changes its name. A plus
operator internally becomes op_Addition. How did we fgure this out?? In a very simple
manner. We created two plus operators that difered only in return type. The compiler gave
us the above error message with the new name of the function.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine(a$b);
}
}
class &&&
{
public static &&& opQ:ddition (&&& a-&&& b)
{
return new &&&();
}
}

(o'piler +rror
a.cs(1-.5)/ error (%!!06/ *perator 3$3 cannot be applied to operands of t&pe 3&&&3 and 3&&&3

Extremely difcult to trick the compiler! We tried and we lost miserably. As mentioned
earlier the compiler converts the plus operator to a function op_Addition. We thought that
by placing such a function, we could trick the compiler into accepting op_Addition as the
plus operator. Unfortunately, it does this name conversion later. It frst checks for a plus
operator, then perform the parameters match-making and if this results in errors then and
only then will it rename the plus operator to op_Addition. It does it at the fag end of the
compilation process.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine(a$b);
}
}
class &&& / 444
243884994.doc 183 od 276
{
public static i'plicit operator string (&&& a)
{
return 9hi9;
}
}
class 444
{
public static 444 operator $ (444 a-444 b)
{
return new 444();
}
public static i'plicit operator string (444 a)
{
return 9b&e9;
}
}

*utput
b&e

An odd thing happens along the way. We've learnt that derived classes inherit everything
form the base class except the constructors and destructors. yyy is derived from xxx and
within class xxx, we overload the plus operator. The derived class yyy does not have a
similar overload, hence the xxx function takes efect. When we add two yyy objects, the
compiler calls the plus operator from the base class, xxx. Since, the resulting object is an
xxx object, while executing System.Console.WriteLine, the string operator from xxx is called
and not the one from yyy.

The only rational explanation for the above behavior is that a yyy object is made up of two
objects, a xxx object and a yyy object. Here the compiler scales down the yyy object to a xxx
object and then calls the plus operator from it. As the returned object is of type xxx, it does
not require the string operator form the class yyy, hence it calls it from xxx. In our humble
point of view, we should have received an error as adding two yyy objects is very diferent
form adding two xxx objects.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
&&& b new &&&();
%&ste'.(onsole.)rite#ine(a$b);
}
}
class &&& / 444
{
public new static 444 operator $ (444 a- &&& b)
{
return new 444();
}
}
class 444
{
public static 444 operator $ (444 a-444 b)
{
return new 444();
}
243884994.doc 184 od 276
}

(o'piler +rror
a.cs(0.-.,)/ error (%!0!5/ =he 'odifier 3new3 is not valid for this ite'

At times, we try very hard to simulate an error. In the books we are writing, we have
promised to display every error message the compiler throws up. The keyword new is used
to hide a method declared earlier in a base class hence it cannot be used in front of a
operator.
The problem with an operator is that it requires at least one of the parameters to be the type
of the class it resides in. Thus we can never ever duplicate the signature of a operator in a
base class in a derived class. As this duplication is not possible, writing the keyword new is
improbable as it cannot hide something that does not exist in the base class. Thus, new is
not required and not allowed as a modifer to an operator.

Summary

We can have only three types of operators. Unary operators, the one that accept only one
operand and can be either in a pre-fx state, that is, before the operator example -x or post-
fx after the operator example x++.

The second type of operator is called a binary operator that accepts two operands and the
operator is used in between the operands. It is also known as the in-fx notation. The most
commonly used example everywhere is x + 10.

The third type of operator is like the Indian tiger, rarely seen or heard. This is the ternary
operator that requires 3 operands and uses the infx notation. There is only one such
operator in existence in the entire C# language. This proves the importance of the ternary
operator to the designers of the C# language. If I was incharge, I would have banished the
only ternary operator ?: to kingdom hell!

Operators can be overloaded. This implies that operators become social beings and are now
capable of interacting with more data types. Overloading operators adds intelligence to the
existing operator thereupon helping them act on diferent user created data types. C# is
oblivious to them. Another interpretation to operator overloading would be using the existing
function of operators on the data types that we create. Operator overloading does not mean
creating new operators.

There are four unary operators +, -, ! and ~ which are allowed to return any type they like,
including the type of the class they reside in. The ++ and the -- can only return the data
type of the class they reside in. This limitation make sense as a ++ is a glorifed addition and
assignment put together. The true and false operators must only return a bool as they
evaluate an expression to either false or true.

For all operators, the signature consists of the operator name or token and its formal
parameter types. The return value and the name of the parameters do not participate in the
operator signature ever. Amongst the unary operators, only the true and false comes in
pairs. Either have both or none at all.

Binary operators can return any type that their hearts desire, there absolutely is no
restrictions whatsoever. There are however three pair wise operators here, == and !=, > and <
and fnally >= and <=.
243884994.doc 182 od 276

We take a simple example of x + y * z * a. The above statement may confuse you but not the
C# compiler. We have a situation where we have multiple operators on a single line. The
compiler does not look at the entire line but checks for the operator of higher importance. It
is familiar with of the order of importance with operators. Thus, between the + and *, C# has
been informed that the * or multiplication is more important that the addition or +. In other
words the precedence of the * is more than that of the +.

Precedence simply defnes the order of importance to C# and this decides which operator is
to be executed frst. C# has a table internally called the precedence table which shows the
relative importance of the operators within each other. The higher you are in this table the
more important C# thinks you are and the earlier you will be recognized in an expression by
the compiler. If you fall near the bottom of the table, wait your turn before being invited for
the ball! Mr Documentation voices the same thing in a better way. It is as follows: "The
precedence of an operator is established by the defnition of its associated grammar
production". Good luck in trying to comprehend what is being said. At the end of the day or
year as the case may be, we are both saying the same thing, albeit in a slightly diferent way.

We now have another problem to resolve. We have the * operator twice on the same line.
Which one will the compiler read frst ? We have a very simple rule. All binary operators are
read forwards. This means that the compiler will only see y * x and then only, their product
is multiplied by z.

Thus the associativity of binary operators is forwards or from left to right. The only two
exceptions to this rule are the assignment operator that moves backwards or right to left
and the only ternary operator. These two operators are called right-associative. This applies
not only to multiple similar operators on the same line but also to operators having the
same precedence. Thus, the rules of precedence decide which operator is to be executed frst
and if by chance many have the same precedence number, then the rules of associativity
apply. Thus, the earlier expression of x + y * z * a would have read as follows - (x + ((y * z) *
a)). These rules of precedence and associativity are now cast in stone like the Ten
Commandments and cannot be changed at all.

Alike everything in life, there is one small caveat. Using the parenthesis, we are allowed to
create our own rules of precedence and associativity, but they are not permanent and only
apply during the expression. Unless stated otherwise, the compiler applies its own
predefned set of rules for operators.

The ther dds and Ends

a.cs
class &&&
{
public static i'plicit operator float(&&& 4)
{
return !;
}
public static i'plicit operator deci'al(&&& 4) {
return !.!';
}
}
class zzz
{
static void Main()
243884994.doc 186 od 276
{
&&& a new &&&();
a Oa;
}
}

(o'piler +rror
a.cs(05-8)/ error (%!!,8/ *perator 3O3 is a'biguous on an operand of t&pe 3&&&3

The operator - can act on a foat and a decimal. We have given the - sign to an object that
looks like yyy. It will try to convert a yyy into a operator that it can deal with. As it is equally
at home with a decimal or a foat, it does not know which one to use and hence the
ambiguity error. If we have two equally probable ways of doing the same thing, we have a
problem.

a.cs
public class zzz
{
public static void Main()
{
int i zzz() G 0/ .;
}
}

(o'piler +rror
a.cs(8-6)/ error (%!006/ 3zzz3 denotes a 3class3 which is not valid in the given conte4t

The above error is similar to the earlier one. Using stuf in the wrong context. A constructor
is not allowed to return vales. We are using the non-existent return value in a ?: operator.
Use things in the wrong place and will return back a diferent error message

a.cs
class zzz
{
public static void Main()
{
string i 9hi9;
i$$;
} }
(o'piler +rror
a.cs(5-0)/ error (%!071/ Fo such operator 3$$3 defined for t&pe 3string3

The designers of the string class have written code in a manner that some of the predefned
C# operators now understand the string class. We call this operator overloading. The
designers of the string class did not overload the ++ operator for reasons best known to
them. Thus, we are also not permitted to use such non-overloaded operators with the string
class.

a.cs
public class zzz {
public static b&te operator true(zzz a)
{
return 0;
}
}

(o'piler +rror
a.cs(,-.!)/ error (%!.08/ =he return t&pe of operator =rue or Ialse 'ust be bool
243884994.doc 187 od 276

The operator true or false must return only a logical value or a bool. There are restriction on
what some operators can return and their parameter types. Fortunately they are few and far
in between.

a.cs
class zzz
{
public static int operator $( char i)
{
}
}

(o'piler +rror
a.cs(,-06)/ error (%!85./ =he para'eter of a unar& operator 'ust be the containing t&pe

An operator overload must have the frst parameter as the data type of the class it belongs
to. A unary operator can have one and only one parameter. Thus, common sense dictates
that with unary operators only, the parameter must be of the class it resides in. Any other
data type as a parameter results in an error.

a.cs
public class zzz
{
public static zzz operator $$ (zzz c)
{
return null;
}
public static int ii
{
set { }
}
public static void Main()
{
opQ@ncre'ent(null);
setQii(0);
}
}

(o'piler +rror
a.cs(0,-0)/ error (%!810/ 3zzz.operator $$(zzz)3/ cannot e4plicitl& call operator or accessor
a.cs(0>-0)/ error (%!810/ 3zzz.ii.set3/ cannot e4plicitl& call operator or accessor

Nearly all the operators or properties or indexers have internal names. This means that the
compiler will convert what we write to something else. In our book C# to IL we have gone
into the depths of these internal names. Thus an operator ++ becomes op_Increment and
the set accessor of a property ii becomes set_ii.

a.cs
class zzz
{
public static void operator $ ( zzz a- zzz b)
{
}
}

(o'piler +rror
a.cs(,-.!)/ error (%!86!/ HserOdefined operators cannot return void
243884994.doc 188 od 276

If a user defned operator returns a void, then we cannot club up a multiple of them
together. The bigger reason is that the operators are supposed to perform operations and
return a value. Of what use is an operator that does not return a values. It is the anti-thesis
of what an operator stands for.

a.cs
public class zzz {
public static void Main()
{
int[] a;
a[);
}
}

(o'piler +rror
a.cs(5->)/ error (%08.8/ @nvalid e4pression ter' 3)3
a.cs(5-8)/ error (%0!!,/ %&nta4 error- 3]3 e4pected

A syntax error will confuse you many a times as the compiler will generate a CS1003 error.
Here it is telling us very clearly what brackets it expects but the frst error is more
misleading.

a.cs
class zzz
{
public static zzz operator $ ( zzz a- zzz b-zzz c)
{
}
}

(o'piler +rror
a.cs(,->>)/ error (%08,>/ *verloaded binar& operator 3$3 onl& taAes two para'eters

There are only three types of operators unary, binary and ternary. The + is called a binary
operator as it takes only two parameters and not three parameters, hence the error.

a.cs
class zzz
{
public static zzz operator $$ ()
{
}
}

(o'piler +rror
a.cs(,-,.)/ error (%08,8/ *verloaded unar& operator 3$$3 onl& taAes one para'eter

In the same way, a unary operator takes only one parameter and not zero. Besides, the
compiler prefers the + to be a binary operator and not a unary one.

a.cs
class zzz
{
public static operator $$ zzz(zzz f)
{
}
}
243884994.doc 189 od 276

(o'piler +rror
a.cs(,-.1)/ error (%088>/ Ceclaration is not valid; use 3"t&pe> operator $$ (...3 instead

The same rules work for operators also. The order is public, static, then the return value,
the reserved keyword operator and fnally the operator character itself. Changing anything is
a recipe for disaster.

a.cs
public class zzz
{
public static void abc()
{
&&& a;
aO>; 0!;
}
}
struct &&&
{
int ;;
}

(o'piler +rror
a.cs(5-0)/ error (%!06,/ =he W or O> operator 'ust be applied to a pointer

The -> operator can only be used by a pointer. A is an object that is an instance of a struct
and in no way a pointer. Certain operators are reserved, like the Nobel prize, to be conferred
on only a selected few.

a.cs
interface iii
{
int operator $(iii aa- iii bb)
{
}
}

(o'piler +rror
a.cs(,-8)/ error (%!851/ @nterfaces cannot contain operators

(nterfaces for reasons unkno#n to us cannot contain o!erators at a. )he ony rationai>ation
is that an o'4ect that ooks ike an interface is not an o'4ect at a as an interface contains no
code. 5hy #oud #e #ant to add t#o o'4ects that do not carry any code. 5e ho!e that the
a'ove is a rationa e$!anation.
Chapter %

&ested Classes

Nested classes are classes created within existing classes.

a.cs
public class zzz
{
class &&&
{
}
243884994.doc 193 od 276
public static void Main()
{
}
}

A simple example is shown above where class yyy is created within class zzz.

a.cs
public class zzz
{
public static void Main()
{
444 4 new 444();
}
}
class &&&
{
class 444
{
}
}

(o'piler +rror
a.cs(8-0)/ error (%!.>5/ =he t&pe or na'espace na'e 34443 could not be found (are &ou 'issing a
using directive or an asse'bl& referenceG)

We are not allowed to use the class xxx directly as it exists inside yyy and has no permanent
existence at all by itself. We cannot even defne an object that looks like xxx.

a.cs
public class zzz
{
public static void Main()
{
&&&.444 a new &&&.444();
}
}
public class &&&
{
public 444 abc()
{
return new 444();
}
public class 444
{
}
}

The actual name of class xxx is yyy.xxx as it is nested within the class yyy.

a.cs
public class zzz
{
public static void Main()
{
&&&.444 a new &&&.444();
%&ste'.(onsole.)rite#ine(a.i);
}
}
public class &&&
{
243884994.doc 191 od 276
public 444 abc()
{
return new 444();
}
public class 444
{
public static int i 0!;
}
}

(o'piler +rror
a.cs(1-.5)/ error (%!015/ %tatic 'e'ber 3&&&.444.i3 cannot be accessed with an instance
reference; use t&pena'e instead

The member i is static, hence we need the name of the class and not name of object.

a.cs
public class zzz {
public static void Main() {
&&&.444 a new &&&.444();
%&ste'.(onsole.)rite#ine(&&&.444.i);
}
}
public class &&&
{
public 444 abc()
{
return new 444();
}
public class 444
{
public static int i 0!;
}
}
*utput
0!

Static members work the same way everywhere. No instance name required, only the name
of class or classes.

a.cs
public class zzz
{
public static void Main()
{
&&&.444 a new &&&.444();
%&ste'.(onsole.)rite#ine(a.;);
}
}
public class &&&
{
public int ; .;
public 444 abc()
{
return new 444();
}
public class 444
{
public int i 0!!;
}
}
243884994.doc 192 od 276

(o'piler +rror
a.cs(1-.5)/ error (%!001/ 3&&&.4443 does not contain a definition for 3;3

The member j is in the class yyy, not in xxx and hence the above error.

a.cs
public class zzz
{
public static void Main()
{
&&&.444 a new &&&.444();
%&ste'.(onsole.)rite#ine(a.p<r());
}
}
public class &&&
{
public int ; .;
public 444 abc()
{
return new 444();
}
public class 444
{
public int i 0!!;
public int p<r()
{
return ;;
}
}
}

(o'piler +rror
a.cs(..-7)/ error (%!!,7/ (annot access a nonOstatic 'e'ber of outer t&pe 3&&&3 via nested t&pe
3&&&.4443

The nested class xxx cannot access the member j in class yyy as it is non-static. The felds
of the containing class are not automatically available to the nested class.

a.cs
public class zzz
{
public static void Main()
{
&&&.444 a new &&&.444();
%&ste'.(onsole.)rite#ine(a.p<r());
}
}
public class &&&
{
public static int ; .;
public 444 abc()
{
return new 444();
}
public class 444
{
public int i 0!!;
public int p<r()
{
return &&&.;;
243884994.doc 193 od 276
}
}
}

*utput
.

The nested class xxx can access static members of the class by simply doing what everyone
else does, that is preface the member with the name of the class.

a.cs
public class zzz {
public static void Main() {
&&&.444 a new &&&.444();
%&ste'.(onsole.)rite#ine(a.p<r());
}
}
public class &&&
{
public int ; .;
public 444 abc()
{
return new 444();
}
public class 444
{
public int i 0!!;
public int p<r()
{
&&& h new &&&();
return h.;;
}
}
}
*utput
.

The non-static members can be accessed as normal but we have to create an object that
looks like yyy.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
}
}
public class &&&
{
public &&&()
{
%&ste'.(onsole.)rite#ine(9&&&9);
}
public class 444
{
public 444()
{
%&ste'.(onsole.)rite#ine(94449);
}
}
243884994.doc 194 od 276
}

*utput
&&&

Creating an object that looks like yyy does not create one that looks like the nested class xxx
as the constructor of class xxx does not get called at all.

a.cs
public class zzz {
public static void Main()
{
&&&.abc();
}
}
public class &&&
{
public &&&()
{
%&ste'.(onsole.)rite#ine(9&&&9);
}
public static void abc()
{
444 4 new 444();
}
public class 444
{
public 444()
{
%&ste'.(onsole.)rite#ine(94449);
}
}
}

*utput
444

The nested class is independent of the containing class it resides in. The static function abc
creates an object that looks like xxx but the constructor of the class yyy is not executed.

a.cs
class zzz {
public class &&&
{
public static int v 6;
}
}
class 444 / zzz
{
public static void Main()
{
zzz a new zzz();
%&ste'.(onsole.)rite#ine(a.&&&.v);
}
}
(o'piler +rror
a.cs(0.-.7)/ error (%!81./ 3&&&3/ cannot reference a t&pe through an e4pression; tr& 3zzz.&&&3
instead

243884994.doc 192 od 276
The compiler comes back with the right answer. As the member v is static, we have no
choice but to use the name of the class zzz and then the name of the inner class.

a.cs
class zzz
{
public class &&&
{
public static int v;
}
}
class 444 / zzz
{
public static void Main()
{
444.&&&.v 8;
}
}

The compiler gives no error messages. We are allowed to access a static member as shown in
the program above.

a.cs
class zzz
{
static void abc(int i)
{
}
static void abc(string s)
{
}
class &&&
{
void p<r()
{
abc(0);
abc(9Dello9);
}
static void abc(long l)
{
}
}
}

(o'piler +rror
a.cs(0>-0)/ error (%08!./ =he best overloaded 'ethod 'atch for 3zzz.&&&.abc(long)3 has so'e
invalid argu'ents
a.cs(0>-8)/ error (%08!,/ :rgu'ent 303/ cannot convert fro' 3string3 to 3long3

The class zzz has two static functions abc that accepts an int and a string. The nested class
yyy creates one more abc that accepts a long. The problem in this is that the two abc's in
class yyy get hidden. The compiler can convert a number one into a long, but cannot convert
the string into a long. Remove the abc from class yyy to see the error disappear.

a.cs
class zzz {
class &&& / zzz {
public static void Main() {
}
}
243884994.doc 196 od 276
}

The above example does not give us a circular reference as the class yyy is derived from
class zzz and it is also a nested class within zzz. A circular reference occurs only if the class
zzz in turn is derived from class yyy directly or indirectly. Remember no one likes circular
references. A class is not dependent upon the classes it nests in.

a.cs
public class zzz {
private static int i> !;
public class &&&
{
public static int i0 .;
internal static int i. 0;
private static int i, !;
}
public class 444
{
public static void Main()
{
zzz.&&&.i0 0;
zzz.&&&.i. .;
zzz.&&&.i, ,;
}
}
}

(o'piler +rror
a.cs(08-0)/ error (%!0../ 3zzz.&&&.i,3 is inaccessible due to its protection level

The same rules binding access modifers apply to nested classes. The feld i1 can be used
everywhere. The feld i2 being internal can be used only in the current project and fnally i3
being private as i4 is used only in class yyy and the containing class zzz.

a.cs
public class zzz
{
private static int i> !;
private class &&&
{
public static int i0 .;
internal static int i. 0;
private static int i, !;
}
public class 444
{
public static void Main()
{
zzz.&&&.i0 0;
zzz.&&&.i. .;
zzz.&&&.i, ,;
}
}
}
(o'piler +rror
a.cs(05-0)/ error (%!0../ 3zzz.&&&.i,3 is inaccessible due to its protection level

Same explanation as above. Only diference the nested class yyy is now private and not
public but nothing changes.

243884994.doc 197 od 276
a.cs
na'espace '''
{
public class zzz
{
private class 444
{
public static int i0 !;
internal static int i. !;
private static int i, !;
}
}
public class &&&
{
public static void Main()
{
zzz.444.i0 0; BB zzz
zzz.444.i. .; BB zzz
zzz.444.i, ,; BB 444
}
}
}

(o'piler +rror
a.cs(05-0)/ error (%!0../ 3'''.zzz.4443 is inaccessible due to its protection level
a.cs(01-0)/ error (%!0../ 3'''.zzz.4443 is inaccessible due to its protection level
a.cs(07-0)/ error (%!0../ 3'''.zzz.4443 is inaccessible due to its protection level

5e have !aced everythin" in a names!ace. ?o# i1 and i2 and i3 are not accessi'e in cass
yyy. i1 and i2 are accessi'e in cass >>> and i3 ony in cass $$$. )he e$!anation has 'een
"iven earier.
Chapter '

(iscellaneous

/embers

There are only two types of entities that can contain members, namely, types and
namespaces. These members can be accessed using the same rules i.e. the name of an
entity to be followed by a dot '.' and then the member name to be given. This cardinal rule is
never ever broken.

All derived classes inherit every member of a base class with the exception of constructors
and destructors. This rule applies to private members also. Regardless of inheriting all the
members, the base class members may/may not be accessible to the derived class members.
Thus, the access modifers have nothing to contribute to inheritance. In other words, the
rules of inheritance ignore the access modifers.

If a namespace is not specifed for a member, it belongs to a default namespace called the
global namespace. Like it or not, all members are part and parcel of a namespace. They
cannot exist out of a namespace under any circumstance. It makes logical sense creating
our own namespaces thereby avoiding the unnecessary clutter in the global namespace with
the members we create.

243884994.doc 198 od 276
The word int is an alias for System.Int32. A variable of type int is internally known as
System.Int32. The basic types like int are internally not known by their name but something
else that is entirely diferent.

a.cs
public class zzz
{
public static void Main()
{
%&ste'.=&pe t t&peof(int);
%&ste'.(onsole.)rite#ine(t.IullFa'e);
}
}

*utput
%&ste'.@nt,.

The above example reconfrms the point we enumerated a little earlier. The compiler does
not understand an int as a data type but knows all about a structure called System.Int32
instead. When we coached you earlier about the compiler understanding about data types
like int at birth, what we truly meant was that there exists structures in the System
namespace relating to these datatypes. The compiler is informed well in advance about the
one-to-one connection between the alias name and their structures. When we get some time
on our hand, we will list out and explain all the members of this structure.

To explain the above example, we use a keyword typeof, which accepts the name of a class or
structure. This returns an object that looks like Type. The Type object consists of methods
and properties that publish everything you ever wanted to know about a type but were
afraid to ask. We are only using one member called FullName to prove our hypothesis.

a.cs
class zzz
{
public static void Main()
{
string &&& 9hell9;
string s &&&;
%&ste'.=&pe t t&peof(&&&);
%&ste'.(onsole.)rite#ine(s);
%&ste'.(onsole.)rite#ine(t);
}
}
class &&&
{
}

*utput
hell
&&&

typeof expects the name of a class as a parameter. C# allows a variable name and a class
name to be the same. At the line s = yyy, C# assumes yyy to be the name of a variable. yyy
is considered to be a class name and t is initialised to the typeof returned by it. Thus, C#
uses a lot of intelligence to read between the lines.

Our resident astrologer informed us that at least 1.54234 per cent of our book must be
copied from the documentation. To make sure that no ill luck befalls us, we copied a couple
243884994.doc 199 od 276
of lines from it. Thus an sbyte is an alias for System.Sbyte,byte System.Byte, short
System.Int16, ushort System.UInt16, uint System.UInt32, long System.Int64, ulong
System.UInt64, char System.Char, foat System.Single, double System.Double, decimal
System.Decimal, and, fnally, bool System.Boolean. Enums are simple derived from
System.Enum and are simple constants. Object is an alias for System.Object and string
System.String. Interfaces are only derived from object and arrays form System.Array.
Delegates are from System.Delegate. These have been mentioned earlier, they have been
placed here only as a ready reckner.

/ember !ookup

A member lookup is a way by means of which the meaning of a name in a type is
determined or fgured out. A class can have base classes and all classes have one base class
object. The compiler frst fgures out all the names that match in the class and base class.
We can have a constant and a property with the same name in a base class and derived
class. We mentioned this earlier, but it merits repeating. If we ever have an override modifer
in front of a member name, then this name has overridden the earlier name in the base
class. It is thus removed from the list as it is a diferent name. If the name is a constant,
feld, property, event, type or enum, it hides all the members of the base class. If it is
function, then all non-methods are hidden in the base class. The end result could either be
a single non-method name or a series of methods. Anything else is an error.

Every known entity is derived from object. We have mentioned this at least a hundred times
already. Value types cannot derive from any other type except object unlike classes and
interfaces. However, arrays and delegates are derived from System.Array and
System.Delegate respectively.

Name Resolution

After the giving a namespace and a period, we can either use the name of another
namespace or the name of a type. If we start, however, with the name of a type, then what
follows can be one of the following. Either a type as in a nested types, or a method, a static
property, static readonly feld or a static feld, static event, constant, or fnally an enum.
Anything else signals an error.

a.cs
class zzz
{
public static void Main()
{
aaa aaa;
aaa bbb aaa.bbb();
aaa ccc aaa.ccc;
}
}
class aaa
{
public static aaa ccc new aaa();
public static aaa bbb()
{
return new aaa();
}
}

243884994.doc 233 od 276
If you want to confuse any person using your code, use the above code. The above
explanation clearly states that we can create a constant, feld, local variable or a parameter
with the same name as the name of a type. Thus, we have a class aaa which contains two
members. One is a static feld called ccc and the other is a static function bbb which
returns an aaa type. In Main, we create a variable called aaa, same as the data type. Then
we call static members of the class aaa using the name of the type. All this leeway is given to
us because we are allowed access to static members of the class aaa without any ambiguity.

An invocation expression can either be a method group or a delegate. Nothing else can be
executable. If the function returns void, the result is nothing. Thus, an expression which
results in nothing cannot obviously be the operand of a operator, it can only be used a
statement.

A method group can either be one method or a group of methods. The parameter types
decide which of the methods would be chosen. To execute a certain method, the compiler
starts with the type before the method. It then proceeds up the inheritance chain and fnds
an applicable, accessible, non-override method. If it fnds more than one, it uses overload
resolution to decide the best one.

)onstructor Signatures

a.cs
public class zzz
{
public static void Main()
{
int i ,;
&&& a new &&&(ref i);
int ;;
&&& b new &&&(out ;);
%&ste'.(onsole.)rite#ine(i $ 9 9 $ ;);
}
}
class &&&
{
public &&&(ref int p)
{
p 0!!;
}
public &&&(out int <)
{
< 0!!!;
}
}

(o'piler +rror
a.cs(07-7)/ error (%!55,/ 3.ctor3 cannot define overloaded 'ethods which differ onl& on ref and
out
a.cs(0>-7)/ (#ocation of s&'bol related to previous error)

A constructor cannot defne overloaded methods difering only on ref or out. The above logic
makes sense as a constructor is nothing but a function that is called automatically at birth.
It is in no way diferent from any other functions that we have worked with.

a.cs
public class zzz
{
243884994.doc 231 od 276
public static void Main()
{
&&& a new &&&(0-.-,);
}
}
class &&&
{
public &&&(para's int [] <)
{
foreach ( int i in <)
%&ste'.(onsole.)rite(i $ 9 9 );
}
}
*utput
0 . ,

We can use the params modifer to our hearts content in displaying the parameters to the
constructors.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&(0-.-,);
}
}
class &&&
{
public &&&(para's int [] <)
{
foreach ( int i in <)
%&ste'.(onsole.)rite(i $ 9 9 );
}
public &&& ( int i- int ; - int A)
{
%&ste'.(onsole.)rite(9int9);
}
}

*utput
int

However, the params modifer is given the last priority by the compiler. If there is an exact
match, a one-to-one with the parameters in the constructors, the compiler gives precedence
to it and the params is ignored for good. For three ints, the constructor with three
parameters will be called and for any other combination of ints, the params constructor will
be called.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&(0-.-,);
}
}
class &&&
{
public &&&(int p- int p0-int p.-para's int [] <)
243884994.doc 232 od 276
{
}
public &&& ( int i- int ; - int A)
{
%&ste'.(onsole.)rite(9int9);
}
}

*utput
int

Regardless of both the constructors matching very closely, the compiler yet does not give us
any error. This is because it treats the params as a second class citizen of the world. We
would never like to be in the shoes of a params parameter.

This

This is only permitted to be used in three places namely a constructor, instance method and
an instance accessor.

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
}
}
class &&&
{
public &&&()
{
%&ste'.(onsole.)rite#ine(this);
if ( this is &&&)
%&ste'.(onsole.)rite#ine(9&es &&&9);
if ( this is zzz)
%&ste'.(onsole.)rite#ine(9&es zzz9);
}
public static i'plicit operator string (&&& a)
{
return 9hi9;
}
}

(o'piler )arning
a.cs(0,-5)/ warning (%!07,/ =he given e4pression is alwa&s of the provided (3&&&3) t&pe
a.cs(08-5)/ warning (%!07>/ =he given e4pression is never of the provided (3zzz3) t&pe

*utput
hi
&es &&&

A this in a constructor is considered to be the value of the data type of the class that
contains the constructor. In the above program, the is operator is used to reconfrm the
above statement. Also, the implicit string operator is called as the this has to be converted to
a string for WriteLine function. Ditto for instance members and accessors. Comment out the
string operator and the output will be

243884994.doc 233 od 276
&&&
&es &&&

For a constructor in a structure, the rules change dramatically. Here it is a variable. Like
earlier, it stands for a reference to the structure it is placed in, but a major diference is that
it is classifed as an out parameter. Thus, all the members of the structure must be
initialized before we leave the constructor.

In the case of an instance method, everything remains the same as above, but now it
behaves as a ref parameter instead of an out. Being a ref parameter, someone else is
responsible for initializing the variable. The function can access all the instance variables in
the structure and also infringe the beneft of changing them if we so choose to. A this is not
used on anything remotely connected with static or in variable initializer of a feld
declaration. A program relating to this is as follows.

a.cs
class zzz
{
int ; 0!;
int i this.;;
public static void Main()
{
}
}

(o'piler +rror
a.cs(>-6)/ error (%!!.1/ Ke&word this is not available in the current conte4t

This variable is available only in certain places within a class type and can be assumed as
the frst parameter to the functions in the type. If we have a function abc accepting two ints
as a parameter, we would write it as abc(int i, int j). In class yyy, the function gets rewritten
as abc( yyy this, int i, int j).

This is passed as the frst parameter and refers to the class the function resides in. As the
programming language C++ called this reference variable as this, C# copied the same
concept but with a small change for structures.

&ase

We use the variable base in a completely diferent manner. A function in the base class gets
hidden by the same function name in a derived class.

a.cs
class zzz
{
public static void Main()
{
aaa a new aaa();
a.abc();
}
}
class &&&
{
public virtual void abc()
{
%&ste'.(onsole.)rite#ine(9&&& abc9);
243884994.doc 234 od 276
}
}
class 444 / &&&
{
public override void abc()
{
%&ste'.(onsole.)rite#ine(9444 abc9);
base.abc();
}
}
class aaa / 444
{
public override void abc()
{
%&ste'.(onsole.)rite#ine(9aaa abc9);
base.abc();
}
}

*utput
aaa abc
444 abc
&&& abc

The base variable has absolutely nothing to do with the modifers new and override. It
simply calls the function in the base class without bothering about new or override to its
existing function. It simply does not care at all. Thus, using base, we can call a function
from the base class irrespective of the modifers on it.

We will get an error if base is used in an abstract function. Base, similar to this, is valid only
in a constructor, instance method or accessor. All the methods from the viewpoint of base,
are non virtual.
Writing base.abc will caste the this pointer of the current class to its lower class, i.e. the
base class. Internally base.abc in class aaa derived from class xxx becomes ((xxx)this).abc
and ditto for an indexer access.

Thus, base and this are conceptually similar, except that one acts on a base class, the other
on a derived class.

a.cs
class zzz
{
public static void Main()
{
444 a new 444();
a.abc();
}
}
class &&&
{
public virtual void abc()
{
%&ste'.(onsole.)rite#ine(9&&& abc9);
}
}
class 444 / &&&
{
public override void abc()
{
243884994.doc 232 od 276
%&ste'.(onsole.)rite#ine(9444 abc9);
((&&&)this).abc();
}
}

Do not run the above program as it will take you to a trip to the moon. Just does not stop at
all. All that we said earlier was only conceptually true. You cannot replace a base with a
this. The programming language contains both of them and cannot be used interchangeably.


A struct variable has a free default constructor always available. Thus unlike a class
initialization, which explicitly initializes the instance variables to zero, the constructor in the
case of the struct does the same.

Name 0iding

Name hiding occurs when we inherit from a class or struct and erroneously or otherwise, we
introduce a similar name as that in the base class. A constant, feld, property, event, or type
hides the base class members with the same name.

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
%&ste'.(onsole.)rite#ine(a.i);
a.i 0!!;
%&ste'.(onsole.)rite#ine(a.i);
}
}
class &&& / 444
{
public int i ,;
}
class 444
{
public const int i 0!;
}

(o'piler )arning
a.cs(0,-0.)/ warning (%!0!7/ =he Ae&word new is re<uired on 3&&&.i3 because it hides inherited
'e'ber 3444.i3

*utput
,
0!!


A million years ago, someone somewhere in the world created a class called xxx. That
gentleman, then, added one const member called i. For some reason, we decided to derive
from class xxx.

We are allowed to create our own variable i notwithstanding the fact that it was declared to
be a const in class xxx. Other than a warning issued by the compiler, we are allowed
complete freedom in using whatever names we like in our derived classes, thus oblivious to
what the base class has given.
243884994.doc 236 od 276

a.cs
public class zzz
{
public static void Main()
{
&&& a new &&&();
a.i();
}
}
class &&& / 444
{
public void i()
{
%&ste'.(onsole.)rite#ine(9hi9);
}
}
class 444
{
public const int i 0!;
}

*utput
hi

As stated earlier, we have total freedom in doing what we like within the derived class. In the
above example, the function i has nothing to do with the const i in class xxx. The compiler
does send you a feeble protest in the form of a simple warning but that can be ignored with
no loss of life or limb. The same rules apply to indexers also. As repeated earlier, operators
can never hide each other ever.

Concealing an inherited member does not issue any error as it would then prevent evolving
any base classes independently. Lets us explain why a warning and not an error.

Let us assume that we are deriving from a base class that has a function called pqr. We now
decided to create an abc function in the derived class. After a while, the next version of the
base class, for some reason, introduces a new function called abc. At that moment, all
derived classes from the base class should not break or give an error. In the scheme of
things followed by C#, they are diferent functions and making changes to a base class does
not invalidate existing derived classes at all.

%unctions

There are fve places in the C# language where we can place executable code. These places
are constructors, methods, properties, indexers and user-defned operators. Anywhere else,
and you permission from the silicon god that is not very forthcoming. Function members
are not members of a namespace and thus we can only place the above in a type. You
cannot have global functions that are not associated with a type. We cannot use ref and out
parameters for indexers, properties or operators. These have to be value parameters only.

a.cs
class zzz
{
public static void Main()
{
zzz a new zzz();
int i !;
243884994.doc 237 od 276
a.abc(i$$-i$$-i$$);
%&ste'.(onsole.)rite#ine(i);
}
public void abc( int 4- int &- int z)
{
%&ste'.(onsole.)rite#ine(4 $ 9 9 $ & $ 9 9 $ z);
}
}
*utput
! 0 .
,

The parameters to a function are read in the order they are written, from left to right. Thus
even though we are using a postfx notation, i++, the compiler uses the current value of i to
initialize parameter x in function abc, and then increase i by one. Thus, i now has a value of
one, that is what the parameter y is initialized to and then it is increased by one. This z
becomes two and at the end of the function invocation variable i has a value of three.

b1ect Elements

a.cs
class zzz
{
public static void Main()
{
b&te [] b new b&te[.];
b[0] 0!;
&&& a new &&&();
%&ste'.(onsole.)rite#ine(b[a]);
}
}
class &&&
{
public static i'plicit operator int(&&& a)
{
return 0;
}
}

*utput
0!

If we remove the overloaded operator int, we get the following error.

(o'piler +rror
a.cs(7-.7)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3&&&3 to 3int3
The variable we use in the [] brackets is called the element access and must be one of the
following data types namely int, uint, long, ulong or any type that can be implicitly
converted to the above type. Thus, without the operator int which was responsible for
converting the yyy to an int, we got the above error. On returning 1 in the operator, the
array variable becomes b[1].

a.cs
class zzz {
public static void Main()
{
b&te [] b new b&te[.];
b[0] 0!;
&&& a new &&&();
243884994.doc 238 od 276
%&ste'.(onsole.)rite#ine(b[a]);
}
}
class &&&
{
public static i'plicit operator uint(&&& a)
{
%&ste'.(onsole.)rite#ine(9uint9);
return 0;
}
public static i'plicit operator long(&&& a)
{
%&ste'.(onsole.)rite#ine(9long9);
return 0;
}
}

*utput
uint
0!

Rules, rules everywhere, but not a drop to drink. The element access of an array as stated
earlier must either be an int, uint, long or a ulong. The above order must be followed. The
compiler checks for the operators in the above order and on fnding the very frst match, it
uses that operator. It stops short in its tracks and does not complain that it could use both
the above operators.
It is one of the few cases where although both the operators were applicable, it does not give
us an error. In the above types, a short is not mentioned. Recall that sometime back, we
spoke of a short being converted into a int. Operators are allowed to throw an exception. If
one takes place then the processing stops.

a.cs
class zzz
{
public static void Main()
{
b&te [] b null;
%&ste'.(onsole.)rite#ine(b[0]);
}
}

*utput
Hnhandled +4ception/ %&ste'.FullJeference+4ception/ Xalue null was found where an instance of
an ob;ect was re<uired.
at zzz.Main()

Like a delegate, if we try and access any null object, an exception is thrown. No compile time
checks are performed for null as the value. Maybe the next version of the compiler will check
for a null and it is on our wish list for Santa Claus. Ditto if we try and exceed the bounds of
an array. The exception thrown is IndexOutOfRangeException instead.

a.cs
class zzz {
public static void p<r(ref ob;ect 4)
{
%&ste'.(onsole.)rite#ine(9p<r 9 $ 4);
}
public static void abc(ob;ect 4)
{
243884994.doc 239 od 276
%&ste'.(onsole.)rite#ine(9abc 9 $ 4);
}
public static void Main() {
ob;ect[] a new ob;ect[.];
ob;ect[] b new string[.];
abc(a[!]);
abc(b[0]);
p<r(ref a[!]);
p<r(ref b[0]);
}
}

*utput
abc
abc
p<r

Hnhandled +4ception/ %&ste'.:rra&=&peMis'atch+4ception/ +4ception of t&pe
%&ste'.:rra&=&peMis'atch+4ception was thrown.
at zzz.Main()

The rules of array co-variance allow an array to be populated by any data type provided
there exist an implicit conversion between them. Array b is an array of objects and there
exists an implicit conversion from a string to an object. Hence, we can initialize an array of
objects to an array of strings. The array object b is not an array of objects but one that
comprises of an array of strings. The compile time data type may be that of an object but the
run time data type must be that of a string.

Whenever we have a ref parameter being passed a reference, we use an array instead. The
compiler is smart enough to perform a run time check on the data type being passed as an
array now can hold dual data types. In the last call to function pqr, we are passing b[0]
which at compile time is an object but at run time is a string. Thus, an exception is thrown.
Remember exceptions can only be thrown at run time. This holds true only for ref
parameters and not value parameters.

a.cs
class zzz {
public static void Main() {
}
int 4;
void abc( int a)
{
4 0;
if (a > 0!)
{
float 4 0.!;
}
}
}

(o'piler +rror
a.cs(0!-1)/ error (%!0,5/ : local variable na'ed 343 cannot be declared in this scope because it
would give a different 'eaning to 343- which is alread& used in a 3parent or current3 scope to
denote so'ething else
a.cs(0!-00)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3double3 to 3int3

A block is represented by a {} braces. Any variable or identifer created within a block must
primarily be unique within that block. Then it must also be unique within the immediate
243884994.doc 213 od 276
enclosing block. Thus in both blocks, the name must be unique and therefore refer to the
same entity. The meaning of the identifer must remain the same within the block.

We get an error in the above program as in the outer block we have an int x and in the inner
block, within the if statement we have another variable called x again. The second error
comes in handy as it very clearly states that it assumes the variable x to be an int within
the if statement. The golden rule again. We cannot have a variable with the same name in
the inner and outer block. If we remove the statement x = 1 from the above program we will
get a slightly diferent error as follows.

(o'piler +rror
a.cs(0!-00)/ error (%!55>/ #iteral of t&pe double cannot be i'plicitl& converted to t&pe 3float3;
use an 3I3 suffi4 to create a literal of this t&pe

a.cs
class zzz
{
public static void Main()
{
}
int 4;
void abc( int a)
{
if (a > 0!)
{
4 0;
}
else
{
double 4 0.!;
}
}
}

(o'piler )arning
a.cs(08-7)/ warning (%!.06/ =he variable 343 is assigned but its value is never used
a.cs(5-8)/ warning (%!056/ =he private field 3zzz.43 is never used

The compiler gives us no errors as the variable x created outside of a function in the outer
block is not used in the main function block. It is only used in the if statement. Since the
else block of the if statement, which is at the same level is not executed, the compiler
adjourns after giving a few warnings.

a.cs
class zzz
{
public static void Main()
{
}
int 4;
void abc( int a)
{
4 ,;
if (a > 0!)
{
4 0;
}
else
{
243884994.doc 211 od 276
double 4 0.!;
}
}
}

(o'piler +rror
a.cs(05-7)/ error (%!0,5/ : local variable na'ed 343 cannot be declared in this scope because it
would give a different 'eaning to 343- which is alread& used in a 3parent or current3 scope to
denote so'ething else
a.cs(05-0.)/ error (%!!.6/ (annot i'plicitl& convert t&pe 3double3 to 3int3

If we knowingly initialize the instance variable x in the function abc, we are using the one
created in the outer block. So, we are not allowed to recreate the same variable x the inner
block under any circumstance. If you comment the line with x=1 or double x=1.0, the
compiler will give you warnings. This proves that the compiler is confused on the variable it
should work with while initializing x to 1 as there are two variables available by the same
name.

a.cs
class zzz
{
public static void Main()
{
}
void abc( bool a)
{
if (a)
{
int i !;
}
int i ,;
}
}

(o'piler +rror
a.cs(0.-8)/ error (%!0,5/ : local variable na'ed 3i3 cannot be declared in this scope because it
would give a different 'eaning to 3i3- which is alread& used in a 3child3 scope to denote so'ething
else
a.cs(0.-8)/ error (%!0!,/ =he na'e 3i3 does not e4ist in the class or na'espace 3zzz3

What we forgot to inform you earlier is that the reverse is not true. That is, a variable
created in an inner block or scope cannot be defned outside the parent or outer scope. This
is in contrast to the above explanation.

a.cs
class zzz
{
public static void Main()
{
}
void abc( bool a)
{
if (a)
{
int i !;
}
if ( a)
{
int i ,;
}
243884994.doc 212 od 276
}
}

We get no error above as the two if statements are at the same level and the variables
created within the blocks are independent of each other. They do not interfere with each
other.

The above explanation, called the rules of invariant, apply only to simple names. It is not
applicable to member access, an example of this is seen below.

a.cs
class zzz
{
public static void Main()
{
}
int 4;
void abc( int 4)
{
this.4 4;
}
}

We have two variables called x in the above program. One is an instance variable and the
other is a parameter to a function. Within the function abc, the parameter variable x hides
the instance variable. If we want to access the instance variable, however, we have to preface
the variable name with this. It is therefore, a good idea to preface all instance variables with
the reserved word this.

$ndexers

a.cs
class zzz
{
public static void Main()
{
&&& a new &&&();
444 b new 444();
a[0] b;
%&ste'.(onsole.)rite#ine(a[0]);
}
}
class &&&
{
public 444 this[int i]
{
get
{
return new 444();
}
set
{
}
}
}
class 444
{
}

243884994.doc 213 od 276
*utput
444

An indexer can store any value provided it is a class, struct or interface. However, the return
value of an indexer is not part of its signature. We can have only one return type but
multiple types of parameters.

Throw

a.cs
class zzz {
public static void Main()
{
throw null;
}
}

*utput
Hnhandled +4ception/ %&ste'.FullJeference+4ception/ +4ception of t&pe
%&ste'.FullJeference+4ception was thrown.
at zzz.Main()

The keyword null is like a rubber man. Fits everywhere. If we throw a null, the actual
exception thrown is NullReferenceException.

a.cs
class zzz
{
public static void Main()
{
throw ;
}
}


(o'piler +rror
a.cs(8-0)/ error (%!085/ : throw state'ent with no argu'ents is not allowed outside of a catch
clause

A throw can be used without the name of an expression only in a catch. This is one of the
few error messages that actually sound English-like. Hope, he is not sacked for committing
blasphemy.

a.cs
class zzz
{
public static void Main()
{
tr&
{
throw null;
}
catch ( %&ste'.+4ception e)
{
%&ste'.(onsole.)rite#ine(9catch9);
throw;
}
}
}
243884994.doc 214 od 276

*utput
catch

Hnhandled +4ception/ %&ste'.FullJeference+4ception/ +4ception of t&pe
%&ste'.FullJeference+4ception was thrown.
at zzz.Main()

In this program, a throw is used without any parameters in a catch statement. It is actually
a short form of the throw and it throws the same exception that the catch invokes.

In this case, the earlier throw showed a NullReferenceException Exception and the inner
throw throws the same exception. Also, remember the end point of a throw can never be
reached even by a miracle. Exception propagation is the process of transferring control from
a throw to one of the exception handlers that may exist.
a.cs
using %&ste';
class zzz
{
void abc()
{
tr&
{
p<r();
}
catch (+4ception e)
{
(onsole.)rite#ine(9abc/ 9 $ e.Message);
e new +4ception(9abc9);
(onsole.)rite#ine(9abc/ 9 $ e.Message);
throw;
}
}
void p<r() {
throw new +4ception(9p<r9);
}
public static void Main()
{
zzz a new zzz();
tr& {
a.abc();
}
catch (+4ception e) {
(onsole.)rite#ine(9Main/ 9 $ e.Message);
}
}
}

*utput
abc/ p<r
abc/ abc
Main/ p<r

The throw by itself re-throws the same exception. The exception parameter is like a value
variable and even though we are re initializing it, the original remains the same. Thus the
message displayed by e.Message does not change from pqr to abc ever. The change is only
afected within the catch block as shown by the WriteLine function. The behaviour is the
same as that of a value variable.

243884994.doc 212 od 276
a.cs
class zzz
{
public static void Main()
{
tr&
{
}
}
}

(o'piler +rror
a.cs(7-0)/ error (%08.>/ +4pected catch or finall&

Man cannot live on love alone. So also, a try needs either a catch or a fnally or both to live.
The variable ofered to a catch is just like a parameter to a function and it can be defnitely
assigned like a value parameter. It also unfortunately dies at the end of the catch. Life-time
and visibility do not exceed the catch block!

a.cs
class zzz
{
public static void Main()
{
tr& {}
catch ( %&ste'.+4ception ) {}
}
}

We do not have to supply the exception name in a catch. If we do not, as in the above
program, there is no known way of fguring out what exception took place. Common sense
dictates that we give the exception a name and try and use it in the code written in the
catch.

a.cs
class zzz
{
public static void Main()
{
tr& {}
catch () {}
}
}

(o'piler +rror
a.cs(5-7)/ error (%0!08/ :n ob;ect- string- or class t&pe e4pected
a.cs(5-0!)/ error (%0!.5/ ) e4pected

What is mandatory however, is the name of the exception class. The documentation says it
is possible to have no parameters and the catch is then called a general catch. Does not
work as advertised for us. The documentation also claims that it has to be the last. No such
luck.

a.cs
class zzz
{
public static void Main()
{
tr& {}
243884994.doc 216 od 276
catch (%&ste'.FullJeference+4ception e) {}
catch (%&ste'.FullJeference+4ception e) {}
}
}

(o'piler +rror
a.cs(1-7)/ error (%!05!/ : previous catch clause alread& catches all e4ceptions of this or a super
t&pe (3%&ste'.FullJeference+4ception3)

You cannot have two catch statements of the same type as the compiler examines them in
textual order, frst come frst serve. The frst match is the chosen exception handler, thus
the second catch would never be called making it unreachable. This is a no-no as the
compiler will have to execute a catch that is unreachable.


a.cs
class zzz
{
public static void Main()
{
zzz a new zzz();
tr& {}
finall&
{
goto aa ;
%&ste'.(onsole.)rite#ine(9aa9);
aa/
goto bb ;
}
bb/
%&ste'.(onsole.)rite#ine(9bb9);
}
}

(o'piler )arning
a.cs(0!-0)/ warning (%!05./ Hnreachable code detected

(o'piler +rror
a.cs(0.-0)/ error (%!081/ (ontrol cannot leave the bod& of a finall& clause

We can use a goto statement to jump around within a fnally. It would be an act of God to
jump out of a fnally. The same rules hold for a break or continue. No leaving a fnally under
any circumstance including a natural calamity. If we have forgotten to tell you this earlier, no
returns help either.

a.cs
class zzz
{
public static void Main()
{
zzz a new zzz();
tr&
{
goto aa ;
}
finall&
{
%&ste'.(onsole.)rite#ine(9aa9);
}
aa/
243884994.doc 217 od 276
%&ste'.(onsole.)rite#ine(9bb9);
}
}

*utput
aa
bb

?o such restrictions a!!y to a try 'ock. 6verythin" that #e tau"ht you earier can aso 'e
used in a try 'ock. )he ony !roviso is that #e have to first e$ecute code in the finay 'ock
'efore eavin" the try 'ock.
Chapter 1)

Compiler Error (essages

We will come across a large number of errors that state Internal Compiler Error. These
errors are extremely dangerous as the compiler itself cannot fgure them out. They fall under
two broad categories.

One, we may have made some dumb mistake which the developers at Microsoft may have
not even thought of, thus confusing the compiler thoroughly.

Secondly, there could be a genuine bug in the compiler.

Take your pick. The frst error number CS0001 is an internal compiler error. A catch all
phrase for 'I do not know what went wrong'.

Thus, the very frst compiler error number is thrown when the compiler is totally confused
and doesn't know how to react to the error. The compiler will resort to this error as the last
error before breaking down. Please understand the irony. The guys who wrote the compiler
could have made it the last error number but symbolically made it the frst.

After studying the diferent programming languages developed so far over a period, we
fgured out that the best way to understand more about any programming language is by
placing ourselves in the shoes of the sole person who wrote the language.

This person did not write the language per se but a compiler through which he expressed
his philosophy of the language. Thus, the only way out is to understand the million error
messages we receive.

This process gets us into the minds of a compiler, thus unearthing what it perceives is right
and wrong with our program. Thus, we have endeavored to sprinkle throughout our books
every error message the compiler can ever throw at us.

After CS0001, we realized that there was no error message CS0002. Since the numerical
part of the error number is the only one that changes, we concluded that there can be a
maximum of only 9999 errors that we can ever make.

In today's world you will rarely come across error number CS0003 as it signifes an out of
memory condition. With RAM so cheap today, we will never ever run out of memory. If we do,
243884994.doc 218 od 276
then the compiler will use our hard disk as memory. This is called virtual memory, and if
you come across a CS0003 then consider yourself blessed.

a.cs
class zzz
{
public static void Main()
{
int i;
}
}

(o'piler )arning
a.cs(8-8)/ warning (%!057/ =he variable 3i3 is declared but never used

A variable when not used in the program gives us a benign warning. Some software
companies hate compilers producing even a single warning. So, to convert warnings into
errors, they implement the /warnaserror option as in

>csc Bwarnaserror a.cs

This produces the same warning in the form of an error.

(o'piler +rror
a.cs(8-8)/ error (%!057/ =he variable 3i3 is declared but never used

As an error is reported, the compiler does not generate an exe fle.

The documentation says that the message number should be CS0004 in the above case. But
we fail to understand it as our error number is reported as CS0168.

a.cs
class zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
{
}

(o'piler +rror
a.cs(0-1)/ error (%!5>8/ @dentifier too long

There are limits to everything in life. In C#, a user defned type cannot exceed 512
characters. These are the limits that the designers imposed on us lowly programmers. Why
on earth would anyone create a type or class name larger than 32 characters is beyond our
imagination. The above information is useful in game shows to test one's knowledge of
unnecessary trivia!

a.cs
class zzz
{
public static void Main()
{
string s O9hell9;
243884994.doc 219 od 276
}
}

(o'piler +rror
a.cs(8-0.)/ error (%!!.,/ *perator 3O3 cannot be applied to operand of t&pe 3string3

Not all operators can be applied to operands of any data type. So, the unary minus operator
cannot be applied to strings. The designers of the string class decided not to have an
overload for the minus operator for reasons known only to them. Thus, operator overloading
lets you teach an old dog new tricks. You can associate an operator to work with operands of
diferent data types.

(o'piler +rror
error (%!!.8/ %tandard librar& file
3(/R)@FC*)%RM@(J*%*I=.F+=RIJ:M+)*JKRX0.!...!>R'scorlib.dll3 could not be found

We received the above error when we renamed mscorlib.dll in the above subdirectory to
xxx.dll. The compiler, csc, expects certain fles in certain directories. If those fles are not
present we get a CS0025 error. The best alternative here is to reinstall the .NET framework.

a.cs
class aaa
{
}
public class zzz
{
public void abc(aaa a)
{
}
public static void Main ()
{
zzz a new zzz();
a.abc(8);
}
}

(o'piler +rror
a.cs(5-0,)/ error (%!!80/ @nconsistent accessibilit&/ para'eter t&pe 3aaa3 is less accessible than
'ethod 3zzz.abc(aaa)3

Thou shall not spend beyond your means is what has been always taught but we never
practiced the adage. Never keep friends that spend more money than you have.
Commonsense? In the above example, the class zzz is public and we are calling a function
abc in it. This function accepts a parameter that looks like aaa. Unfortunately, as the class
aaa has no access modifers the default is internal. The class zzz as mentioned earlier is
public, so it is higher in accessibility than internal. They need to have the same or higher
accessibility. The only way out is to remove public from zzz or add a public to class aaa.
Also, the class aaa can be public and class zzz may be internal and all yet work fne.

So, as we said before, equalize to higher accessibility not lower.

a.cs
class aaa
{
}
public class zzz
{
public aaa p0
243884994.doc 223 od 276
{
get
{
return new aaa();
}
}
}

(o'piler +rror
a.cs(5-0.)/ error (%!!8,/ @nconsistent accessibilit&/ propert& t&pe 3aaa3 is less accessible than
propert& 3zzz.p03

The same rules apply everywhere. We cannot return an object aaa from a property p1 as it is
less accessible than the class zzz.


a.cs
class aaa
{
}
public class zzz
{
public aaa this[int i]
{
get
{
return new aaa();
}
}
}

(o'piler +rror
a.cs(5-0.)/ error (%!!8>/ @nconsistent accessibilit&/ inde4er return t&pe 3aaa3 is less accessible
than inde4er 3zzz.this[int]3

Ditto for indexers. They have a separate error message for each concept even though a
common one would sufce.

a.cs
class aaa
{
}
public class zzz
{
public int this[aaa i]
{
get
{
return !;
}
}
}

(o'piler +rror
a.cs(5-0.)/ error (%!!88/ @nconsistent accessibilit&/ para'eter t&pe 3aaa3 is less accessible than
inde4er 3zzz.this[aaa]3


The same rules that hold good for return values also stand true for parameter types as an
indexer can have any type as a parameter.
243884994.doc 221 od 276

To stop further repetition, if an operator returns an aaa in the above case we would get
CS0056 and a CS0057 if the parameter is inaccessible. CS0058 deals with delegates and
CS0059 for parameters to delegates.

a.cs
class aaa
{
}
public class zzz / aaa
{
}

(o'piler +rror
a.cs(>-0>)/ error (%!!5!/ @nconsistent accessibilit&/ base class 3aaa3 is less accessible than class
3zzz3

We should never ever enter into a marriage with someone far better than us. It simply does
not work! In the same way, we cannot derive from a class that is less accessible than we are.
In this case, aaa is internal and we are public. Lest you have forgotten, public has no rules
associated with it whereas internal applies to the current source fle only.

a.cs
interface aaa
{
}
public interface zzz / aaa
{
}

(o'piler +rror
a.cs(>-07)/ error (%!!50/ @nconsistent accessibilit&/ base interface 3aaa3 is less accessible than
interface 3zzz3

The same rules apply to an interface as to a class. We only have a separate error message.
a.cs
using %&ste';
using %&ste';

(o'piler +rror
a.cs(.-1)/ warning (%!0!8/ =he using directive for 3%&ste'3 appeared previousl& in this na'espace

For various reasons we are not allowed to use the same using directive more than once in
our program. There are many things in life that one just does not understand. It does not
cause anyone any harm if we include the same using twice. Many people don't do things
unless told twice. But, who are we to argue with a compiler?!

a.cs
class zzz / &&&
{
new int i;
}
class &&&
{
}

(o'piler )arning
243884994.doc 222 od 276
a.cs(,-6)/ warning (%!0!6/ =he 'e'ber 3zzz.i3 does not hide an inherited 'e'ber. =he new
Ae&word is not re<uired.

The keyword new is used for versioning. It lets you create a new member altogether and has
nothing to do with the ones having the same name in the base class. In this case, i is not
present in the base class but yet we have used the keyword new with i in zzz. Here the new
keyword is redundant but the compiler gives us only a warning and not an error. A warning
is something that is not at all serious.

a.cs
class zzz / &&&
{
public virtual override void abc()
{
}
}
class &&&
{
public virtual void abc()
{
}
}

(o'piler +rror
a.cs(,-,!)/ error (%!00,/ : 'e'ber 3zzz.abc()3 'arAed as override cannot be 'arAed as new or
virtual

Fire and water do not mix with each other. In the same way there are certain modifers
which cannot be used in conjunction with each other. When we override a function, it
replaces a function with the same name in a base class. Also, if we add the new modifer,
then we are saying that it overrides the base class function. A contradiction of the frst
order!

A function can only be overridden if it is marked virtual in the base class. No point in saying
it again. Override means replace the function in the base class. At the same time if we use
the modifer abstract, then we are saying the function has no code. How on earth can we
override a function in the base class with one that has no code at all? Two wrongs do not
make a right!

a.cs
class zzz / &&& {
public override void abc()
{
}
}
class &&&
{
public void abc(int i)
{
}
}



(o'piler +rror
a.cs(.-..)/ error (%!008/ 3zzz.abc()3/ no suitable 'ethod found to override

243884994.doc 223 od 276
A function is not known by its name but by its signature. In this case we have two functions
carrying the same name but with diferent signatures. Thus in class zzz there is no suitable
function abc in class yyy to override and hence the error. We are asking the compiler to
override a function but there is no such function in the base class to override. In the case of
new, we only received a slap on our wrists, a warning. Here we get an error instead.

a.cs
public class zzz {
static zzz(int o)
{
}
}

(o'piler +rror
a.cs(.-7)/ error (%!0,./ 3zzz.zzz(int)3/ a static constructor 'ust be para'eterless

Static constructors are a new concept in programming languages. They are called at the
time of loading the class and hence we can never call them, only the system calls them. The
system never calls them with parameters and hence the above error indicates that a static
constructor cannot accept parameters.

a.cs
class zzz
{
public static int i .;
public static void Main()
{
{
int i 5;
}
i$$;
}
}
(o'piler +rror
a.cs(6-0)/ error (%!0,8/ 3i3 conflicts with the declaration 3zzz.i3

The problem here is a logical one and not one of syntax. The variable i created in function
Main dies at the end of the brace. Then the static i created outside Main is incremented.
What the compiler is upset about is the fact that in the Main function it encounters two
variables with the same name i. Being uncomfortable with this situation, it drops an error.

The meaning of a variable must not be changed within a function, thus name hiding
generates the above error.

a.cs
class &&&
{
}
class zzz
{
public static void Main()
{
tr& {}
catch ( &&& a) {}
}
}

(o'piler +rror
243884994.doc 224 od 276
a.cs(6-6)/ error (%!088/ =he t&pe caught or thrown 'ust be derived fro' %&ste'.+4ception

The catch clause takes a parameter which is either an instance of a class System.Exception
or a class derived from System.Exception. Here yyy is not derived from System.Exception
and hence the error.

a.cs
class zzz {
int i;
public int ;;
public static void Main() {
}
}
(o'piler +rror
a.cs(.-8)/ warning (%!056/ =he private field 3zzz.i3 is never used
a.cs(,-0.)/ warning (%!5>6/ Iield 3zzz.;3 is never assigned to- and will alwa&s have its default
value !

a private member and a public member when not used gives two diferent warnings. The
second warning very clearly informs us that the public variable has been initialized to zero,
which does not happen in the case of private variables.

a.cs
class zzz
{
int i;
public int ;;
public static void Main()
{
zzz a new zzz();
%&ste'.(onsole.)rite#ine(a.i$9 9 $ a.;);
}
}

(o'piler )arning
a.cs(,-8)/ warning (%!5>6/ Iield 3zzz.i3 is never assigned to- and will alwa&s have its default value
!
a.cs(>-0.)/ warning (%!5>6/ Iield 3zzz.;3 is never assigned to- and will alwa&s have its default
value !

*utput
! !

However, if we create an object that looks like zzz, then the compiler does not distinguish
between public and private and gives the same warning. Contrast it with the situation
earlier. You can fgure out these diferent warnings yourselves.

a.cs
struct &&&
{
public int i;
}
class zzz
{
public static void Main()
{
&&& a;
%&ste'.(onsole.)rite#ine(a.i);
}
243884994.doc 222 od 276
}

(o'piler +rror
a.cs(0!-.5)/ error (%!01!/ Hse of possibl& unassigned field 3i3

The designers of the C# language investigated as to why programs fail very often. They came
about with the conclusion that programmers hate initializing variables. These variables,
thus, have a random value when they are being used. To eliminate such errors, the
designers forbade us from using a variable without giving it an initial value. A structure is
also a collection of variables and what applies to one variable also applies to a collection.
Thus, the compiler makes sure that each and every variable is given a value before we can
use them.

a.cs
class zzz
{
public static void Main()
{
base 0!;
%&ste'.(onsole.)rite#ine(base);
}
}

(o'piler +rror
a.cs(8-0)/ error (%!018/ Hse of Ae&word base is not valid in this conte4t
a.cs(5-.5)/ error (%!018/ Hse of Ae&word base is not valid in this conte4t

Like it or not, all objects are derived from a base class called Object. If we happen to be in a
derived class and we want to access a base class member irrespective of whether it is hidden
or not, the base keyword is to be used. This keyword is to be used to access base class
members only.

a.cs
class zzz
{
public void abc(out int i)
{
}
}

(o'piler +rror
a.cs(,-0,)/ error (%!011/ =he out para'eter 3i3 'ust be assigned to before control leaves the
current 'ethod

An out parameter must be initialized before the function ends. This is because an out
parameter does not have to be initialized at the time of calling the function. The compiler
guarantees this and ensures that the out variable has been given a value. The reverse
applies to ref also. We have to give ref parameters a value before calling a function.

a.cs
class zzz
{
public e4tern void abc()
{
}
}

(o'piler +rror
243884994.doc 226 od 276
a.cs(,-.!)/ error (%!016/ 3zzz.abc()3 cannot be e4tern and declare a bod&

Whenever we specify a function as extern, it is our way of telling the compiler that the code
of the function is located in a diferent fle. Hence, we should not be allowed to add code to
our function. If we do break this rule, we will get the above error. Extern functions cannot
have a defnition.


a.cs
class zzz
{
public abstract e4tern void abc();
}

(o'piler +rror
a.cs(,-.6)/ error (%!07!/ 3zzz.abc()3 cannot be both e4tern and abstract

The modifers extern and abstract both fnally mean the same. No code allowed for the
function. In the case of abstract, the code will be supplied by the derived class and in extern
from some other place, maybe outside the current fle. Thus abstract and extern are
mutually exclusive.

a.cs
public class zzz
{
static string s;
[%&ste'.Ciagnostics.(onditional:ttribute(s)]
static void Main()
{
}
}

(o'piler +rror
a.cs(>->.)/ error (%!07./ :n attribute argu'ent 'ust be a constant e4pression- t&peof
e4pression or arra& creation e4pression

The parameters passed to an attribute can be one of the types as mentioned in the above
error. Thus we cannot use a variable as a parameter in the above case even though we are
giving it a string data type, which the parameter to the attribute expects it to be.

a.cs
class zzz
{
public static void Main()
{
if (((%&ste'.*b;ect) null) is string)
;
}
}

(o'piler +rror
a.cs(8-1)/ error (%!075/ Hse of null is not valid in this conte4t

A null represents no object at all. Thus we are not allowed to cast null into any other data
type.

a.cs
struct &&&
243884994.doc 227 od 276
{
int ;;
&&&(int i)
{
abc();
; i;
}
void abc()
{
}
}

(o'piler +rror
a.cs(5-0)/ error (%!077/ =he this ob;ect cannot be used before all of its fields are assigned to

In a structure, we must frst initialize all the members of the structure before calling a
function within it. Remember all references are prefaced by a this. Calling the function abc
in yyy is equivalent to this.abc().

a.cs
struct &&&
{
&&&(int i)
{
abc();
}
void abc()
{
}
}

(o'piler +rror
a.cs(8-0)/ error (%!077/ =he this ob;ect cannot be used before all of its fields are assigned to

So pigheaded is the compiler that even though we have no variables in the structure it yet
gives us an error. The compiler does not realize that we have no variables at all in the
structure. Maybe the next version of the compiler will not give us such an error.

The only explanation we can provide is that if we have a constructor with parameters, then
we have to initialize at least one variable. This also means we need at least one variable in
the structure.

a.cs
class zzz
{
public readonl& int i 0!;
zzz()
{
i ,;
}
public static void Main()
{
zzz a new zzz();
a.i >;
}
}

(o'piler +rror
243884994.doc 228 od 276
a.cs(00-0)/ error (%!060/ : readonl& field cannot be assigned to (e4cept in a constructor or a
variable initializer)

A readonly variable can only be initialized in a constructor or at the time of creation.
Anywhere else is a no-no as it would defeat the very concept of being readonly.

a.cs
class zzz
{
public static readonl& int i 0!;
zzz()
{
i ,;
}
public static void Main()
{
}
}

The error reported is the same as before as the readonly variable i is static and the
constructor in which it is being initialized is not static. Add the static modifer to the
constructor and watch the error fy away.

a.cs
class zzz
{
public static readonl& int i 0!;
public static void abc( ref int ;)
{
}
public static void Main()
{
zzz.abc(ref i);
}
}

(o'piler +rror
a.cs(6-0,)/ error (%!066/ : static readonl& field cannot be passed ref or out (e4cept in a static
constructor)

A static readonly feld cannot be passed as a parameter to a function like a normal readonly
variable.

A statement comprises fve entities: an assignment, increment, decrement, call and a new.

a.cs
public class zzz
{
public void Net+nu'erator ()
{
}
public static void Main ()
{
zzz z new zzz();
foreach (zzz 4 in z)
{
}
}
}
243884994.doc 229 od 276

(o'piler +rror
a.cs(6-0)/ error (%!.!./ =he call to Net+nu'erator 'ust return a class or a struct- not 3void3

The GetEnumerator function must return a class or a struct, surely not a void.

One error that we did not have the heart to shown you is CS0204. This one pops up when
you have more than 65535 local variables in your program. Who on earth has the time to
create so many variables and we doubt whether anyone on earth has ever seen this error
message? Trust is what makes the world go round and we trust the documentation.

a.cs
public class zzz
{
public static void abc(para's int[] a- QQarglist)
{
}
}

(o'piler +rror
a.cs(,-.>)/ error (%!.,0/ : para's or QQarglist para'eter 'ust be the last para'eter in a for'al
para'eter list

We cannot use the __arglist and params together as parameters to a function. Either one of
it is used. This is because both of them are an array and they mop up whatever is left over
of the parameters. The __arglist is used to access parameters passed to a function.

a.cs
public class zzz
{
public static void Main()
{
int i;
%&ste'.(onsole.)rite#ine(sizeof(i));
}
}

(o'piler +rror
a.cs(5-.5)/ error (%!.,,/ sizeof can onl& be used in an unsafe conte4t (consider using
%&ste'.Junti'e.@nterop%ervices.Marshal.%ize*f)

The sizeof is a remnant of the C programming language. Hence we can only use it in an
unsafe context. In our opinion, these are unnecessary restrictions placed on us by the
compiler. Who listens to the underdog?

a.cs
public class zzz
{
public static void Main()
{
int i 0!;
%&ste'.(onsole.)rite#ine(%&ste'.Junti'e.@nterop%ervices.Marshal.%ize*f(i));
}
}

*utput
>

243884994.doc 233 od 276
We took the advise of the error message and used the SizeOf function in the class Marshal.
We did receive the same answer that sizeof would have given us in unsafe conditions.
The only hitch here is that i must be initialized in advance. Such is not the case with the
sizeof keyword. This is one of the major diference between a function and a keyword.
Something that the language understands is treated diferently from a parameter to a
function.

a.cs
abstract class zzz
{
public abstract void abc()
{
}
}

(o'piler +rror
a.cs(,-..)/ error (%!8!!/ 3zzz.abc()3 cannot declare a bod& because it is 'arAed abstract

The concept of abstract expects the derived class to write the code for the abstract
functions. Thus, we cannot write the code ourselves by specifying a body.

a.cs
abstract class zzz
{
public virtual abstract void abc();
}

(o'piler +rror
a.cs(,-,!)/ error (%!8!,/ =he abstract 'ethod 3zzz.abc()3 cannot be 'arAed virtual

The compiler hates repetition. An abstract function is by default virtual and it gets upset if
you specify virtual again. Maybe the next version should be a lot more considerate when we
state the obvious again. Honestly, this would slim down the number of error messages.

a.cs
class zzz / &&& {
public override void abc()
{
}
}
class &&&
{
public int abc;
}

(o'piler +rror
a.cs(.-..)/ error (%!8!8/ 3zzz.abc()3 / cannot override; 3&&&.abc3 is not a function

We can only override a function provided a function with the same name is present in the
base class. Here abc is an int in the base class, not a function name and hence the error.

a.cs
class zzz / &&&
{
static zzz() / base()
{
}
}
243884994.doc 231 od 276
class &&&
{
}

(o'piler +rror
a.cs(,-7)/ error (%!80>/ 3zzz.zzz()3/ static constructor cannot have an e4plicit this or base
constructor call

A static constructor is not allowed to call the static constructor of the base class using the
keyword base. The problem with static is that anything to do with it has to be known at
compile time only whereas the keyword this has a value only at run time. Plus, static
functions are not passed this and base as parameters.

a.cs
class zzz
{
public static zzz()
{
}
}

(o'piler +rror
a.cs(,-08)/ error (%!808/ 3zzz.zzz()3/ access 'odifiers are not allowed on static constructors

A static constructor is called when the object is loaded. Thus, as it is an internal thing, we
are not allowed to use any access modifers on static constructors.

a.cs
class zzz
{
public zzz() / this()
{
}
}

(o'piler +rror
a.cs(,-7)/ error (%!805/ (onstructor 3zzz.zzz()3 cannot call itself

A constructor can use the keyword this and base to call any other constructor but itself.

a.cs
class zzz {
public zzz() / this(0!)
{
%&ste'.(onsole.)rite#ine(9Xoid9);
}
public zzz(int i) / this()
{
%&ste'.(onsole.)rite#ine(9*ne9);
}
public static void Main()
{
%&ste'.(onsole.)rite#ine(9%tart9);
zzz a new zzz();
}
}
*utput
%tart

243884994.doc 232 od 276
In our belief, the compiler does not try hard enough to detect circular references. Here the
constructors are blindly calling each other and the compiler is blind to their doings. Thus, it
is easy to go into an indefnite loop while writing code. At times, we may receive a runtime
error stating: Unhandled Exception: StackOverfowException.

a.cs
struct zzz
{
public zzz(int i) / base(i)
{
}
}

(o'piler +rror
a.cs(,-7)/ error (%!8../ 3zzz.zzz(int)3/ structs cannot call base class constructors

Structures cannot call the base class constructors as they in their very nature are sealed.
Hence, what the structure derives from cannot be called also. The other reason could be
that they are created on the stack. Thus, structures and classes in a sense are a lot similar
but a lot dissimilar too.

a.cs
struct zzz
{
&&& a;
}
struct &&&
{
zzz b;
}

(o'piler +rror
a.cs(1-8)/ error (%!8.,/ %truct 'e'ber 3&&&.b3 of t&pe 3zzz3 causes a c&cle in the structure la&out
You rely on someone to do a job and he relies on you to do the same job. Chaos is the result.
Hence, in the struct zzz we have a member a that is an instance of a type yyy and struct yyy
in turn has a member called b that is an instance of zzz. This causes a cycle. The complier
thus throws its hands up in the air and gives up. Cyclic references are like plague

a.cs
interface iii
{
public class zzz
{
}
}

(o'piler +rror
a.cs(,-0>)/ error (%!8.>/ 3zzz3/ interfaces cannot declare t&pes

You can do lots of things in an interface but you cannot create your own class within them.
A nested class is not permissible in an interface. The reason for the restriction is that
classes carry code with them and an interface cannot have any code in it.

a.cs
interface iii
{
int i;
}
243884994.doc 233 od 276

(o'piler +rror
a.cs(,-0)/ error (%!8.8/ @nterfaces cannot contain fields

Interfaces are all fuf. They cannot have anything concrete. They are only rules that others
have to obey. Thus, a feld, which is something fnished, something complete cannot reside
in an interface.

a.cs
interface iii {
iii();
}
(o'piler +rror
a.cs(.-0)/ error (%!8.5/ @nterfaces cannot contain constructors

Interfaces cannot have a whif of code. So having constructors is out of question. It cannot
also have the defnition or a prototype of a constructor as it will then have to be flled with
code. Thus, constructors are not allowed to have a constructor defnition.

a.cs
interface iii
{
}
class zzz / iii-iii
{
}

(o'piler +rror
a.cs(>-01)/ error (%!8.7/ 3iii3 is alread& listed in interface list

The compiler would hate seeing a movie with a double role. You cannot derive from a
interface twice. A class can derive from multiple interface but not from the same one twice.
Makes sense. Why would anyone do something so foolhardy?

a.cs
interface iii / ;;; {
}
interface ;;; / iii
{
}

(o'piler +rror
a.cs(0-00)/ error (%!8.6/ @nherited interface 3;;;3 causes a c&cle in the interface hierarch& of 3iii3

Anything cyclic is a problem for the compiler. Here we will go on and on in a indefnite loop
as interface iii depends upon interface jjj which again depends upon iii.

a.cs
abstract public class zzz
{
abstract public void abc();
}
abstract public class &&& / zzz
{
new abstract public void abc();
}

(o'piler +rror
a.cs(1-.5)/ error (%!8,,/ 3&&&.abc()3 hides inherited abstract 'e'ber 3zzz.abc()3
243884994.doc 234 od 276

Class yyy is derived from zzz and they both have an abstract function called abc. In the
class yyy abc has a new modifer, thus it has nothing to do with the base class abc. The
problem is that the base class abc has no code at all and thus making the yyy class abc new
makes no sense. New assumes that we are creating a diferent function over the base class.

a.cs
public class zzz / &&&
{
public static void abc()
{
}
}
interface &&&
{
void abc();
}

(o'piler +rror
a.cs(0-0>)/ error (%!8,5/ 3zzz3 does not i'ple'ent interface 'e'ber 3&&&.abc()3. 3zzz.abc()3 is
either static- not public- or has the wrong return t&pe.

Any mistake on our part gives an error. Interface yyy contains a function abc which is to be
implemented in zzz. But while implementing it in zzz, we made it static. The compiler is very
clear on one point. Implement the entire function including return type and access modifers
as they are in the interface otherwise conk out. No deviations from the original is allowed
under any circumstances.

a.cs
class zzz / &&&
{
void &&&.abc()
{
}
}
class &&&
{
}

(o'piler +rror
a.cs(,-5)/ error (%!8,7/ 3&&&3 in e4plicit interface declaration is not an interface

We are allowed to specify the interface name before a function with a dot as a separator. This
is because we are allowed to implement a class from multiple interfaces, which may carry
the same function name. However, yyy is the name of a class and not a interface.

a.cs
interface zzz / &&&
{
void &&&.abc();
}
interface &&&
{
void abc();
}

(o'piler +rror
243884994.doc 232 od 276
a.cs(,-5)/ error (%!8>0/ 3&&&.abc3/ e4plicit interface declaration can onl& be declared in a class or
struct

We are only allowed to use the explicit interface notation in a class or a structure. Within
another interface, it is not accepted. This speaks of sound common sense as an interface
can never ever carry code and thus cannot carry the defnition or code of the function
either. Thus specifying the interface the function dwelled in does not make any sense.

a.cs
class zzz / &&&
{
public override int i
{
set {}
}
}
class &&&
{
public int i;
}

(o'piler +rror
a.cs(,-.0)/ error (%!8>>/ 3zzz.i3 / cannot override; 3&&&.i3 is not a propert&

We can have a property called i in the derived class and a variable called i in the base class.
The compiler can live with it and they can both coexist. The problem starts the moment we
override the property i in the derived class. This is not allowed as we have no property by
the same name in the base class. We have only a lowly variable.

a.cs
class zzz / &&&
{
public override int i
{
set {}
get { return !;}
}
}
class &&& {
virtual public int i
{
set { }
}
}
(o'piler +rror
a.cs(5-0)/ error (%!8>8/ 3zzz.i.get3/ cannot override because 3&&&.i3 does not have an overridable
get accessor

A property does not exist by itself. It comes along with a get and set accessor. You cannot
exceed what the designers chose to implement in the base class. Only things that are virtual
in the base class can be overridden. So, the only way out of the above quandary is to defne
a get like the set.

a.cs
sealed class zzz
{
public virtual void abc()
{
}
243884994.doc 236 od 276
}

(o'piler +rror
a.cs(,-.0)/ error (%!8>6/ 3zzz.abc()3 is a new virtual 'e'ber in a sealed class 3zzz3

The sole purpose of virtual in life is to ensure overriding of a function in he base class
within the derived class. A sealed class cannot be used as a base class and hence virtual
functions are a contradiction in terms.

a.cs
class zzz / iii
{
int iii.i
{
set {}
}
}
interface iii
{
int i
{
get;
}
}
(o'piler +rror
a.cs(,-8)/ error (%!880/ +4plicit interface i'ple'entation 3zzz.iii.i3 is 'issing accessor 3iii.i.get3
a.cs(8-0)/ error (%!88!/ 3zzz.iii.i.set3 adds an accessor not found in interface 'e'ber 3iii.i3
a.cs(0-1)/ error (%!8,8/ 3zzz3 does not i'ple'ent interface 'e'ber 3iii.i.get3

In an interface we have a property i with a get and no set. In the derived class, we have not
added the same property but instead given a set property. The compiler throws error
messages as it feels we are confused with what we were trying to achieve.

a.cs
public class zzz
{
public Z&&&()
{
}
}

(o'piler +rror
a.cs(,-6)/ error (%!81>/ Fa'e of destructor 'ust 'atch na'e of class

A destructor is called a destructor because it is of the same name as the name of the class.
You cannot have any other name starting with a ~ in a class.

a.cs
interface iii
{
void abc();
}
public class zzz / iii
{
[%&ste'.Ciagnostics.(onditional:ttribute(9hi9)]
void iii.abc()
{
}
}
243884994.doc 237 od 276

(o'piler +rror
a.cs(1-.)/ error (%!811/ (onditional not valid on 3zzz.iii.abc()3 because it is a constructor-
destructor- operator- or e4plicit interface i'ple'entation

An attribute cannot be used anywhere and everywhere. Fools rush in where angels fear to
tread. Thus, we cannot use the ConditionalAttribute on a constructor, destructor, operator,
or explicit interface implementation. The error message informs us the same.

a.cs
public class zzz
{
[%&ste'.Ciagnostics.(onditional:ttribute(9hi9)]
int abc() {
}
}

(o'piler +rror
a.cs(,-.)/ error (%!817/ (onditional not valid on 3zzz.abc()3 because its return t&pe is not void

When creating an attribute you can be extremely picky about where it can be used. The
ConditionalAttribute can be used only above functions that return void. One more rule to
remember!

a.cs
using %&ste'.Junti'e.(o'piler%ervices;
public class zzz
{
[@nde4erFa'e(9abc9- 9def9)]
public int this [int inde4]
{
set {}
}
}

(o'piler +rror
a.cs(,-0>)/ error (%!87!/ =oo 'an& unna'ed argu'ents to attribute 3@nde4erFa'e3
Attributes have their own set of rules and regulations. The designers of the IndexerName
attribute allowed only a single parameter of a string data type. We are passing it two strings
and hence the error.

a.cs
using %&ste'.Junti'e.(o'piler%ervices;
public class zzz
{
[@nde4erFa'e(9abc9- vi;a&.)]
public int this [int inde4]
{
set {}
}
}

(o'piler +rror
a.cs(,-.)/ error (%!870/ Fa'ed argu'ents not allowed on attribute 3@nde4erFa'e3

We are also not allowed to use named arguments, as the attribute does not understand
them.

243884994.doc 238 od 276
a.cs
interface iii
{
[%&ste'.Ciagnostics.(onditional:ttribute(9C+?HN9)]
void zz();
}

(o'piler +rror
a.cs(,-.)/ error (%!87./ (onditional not valid on interface 'e'bers

We are not allowed to use the ConditionalAttribute on an interface member. We can decide
with the precision of a stealth bomber where an attribute is allowed. This is one more
restriction on an interface member.

a.cs
using %&ste'.Junti'e.@nterop%ervices;
[%truct#a&out(#a&outKind.%e<uential V #a&outKind.+4plicit)]
class zzz
{
}

An attribute cannot have options, which are contradictory in the defnition of the attribute.
In the above case, both of them are valid.

a.cs
class zzz
{
void this [int i]
{
set {}
}
}

(o'piler +rror
a.cs(,-5)/ error (%!5.!/ @nde4ers cannot have void t&pe

Indexers, Properties, Operators cannot have void return types as they are diligent in the job
they perform and always return values.

a.cs
class zzz
{
virtual void abc()
{
}
}

(o'piler +rror
a.cs(,-0>)/ error (%!5.0/ 3zzz.abc()3 / virtual or abstract 'e'bers cannot be private

By default, a member in a class is marked private. This prohibits the derived classes from
accessing its private functions.

A virtual function allows derived classes to override its function whereas an abstract
function is implemented in a derived class. Hey, wait a minute! A private function cannot be
accessed in a derived class as we mentioned a couple of lines earlier. Thus, a private
function cannot be tagged with the modifers virtual and abstract.

243884994.doc 239 od 276
a.cs
using %&ste'.Junti'e.@nterop%ervices;
[%truct#a&out(#a&outKind.+4plicit)]
struct zzz
{
[Iield*ffset(8)]
public int i;
public int ;;
}

(o'piler +rror
a.cs(1-0.)/ error (%!5.8/ 3zzz.;3 / @nstance field t&pes 'arAed with
%truct#a&out(#a&outKind.+4plicit) 'ust have a Iield*ffset attribute.

The StructLayout attribute lets you decide on the memory layout for the structure. Normally
the compiler decides where in memory the individual felds will start but at certain times,
we would like to decide where in memory the felds should begin.

The parameter to the attribute, which will give us complete freedom in laying out the felds
in memory is called LayoutKind.Explicit. This freedom comes at a price. We have to
individually specify where each feld starts using the FieldOfset Attribute. This is no
partnership where we supply some and the compiler does the rest. The destiny of the
structure is now in our hands. We cannot miss out any feld as we did in the above example
with variable j.

a.cs
sealed class zzz
{
protected void abc()
{
}
public static void Main()
{
}
}
(o'piler +rror
a.cs(,-05)/ warning (%!5.7/ 3zzz.abc()3 / new protected 'e'ber declared in sealed class

Remember warnings are not life-threatening at all. A protected member only allows a derived
class to use a function and a sealed class cannot be derived from, hence the compiler thinks
we are confused or lost. May be it thinks we should have used private instead as in this case
the protected gets downgraded to private access. No big deal and thus a warning.

a.cs
class zzz / iii
{
[%&ste'.Ciagnostics.(onditional(9debug9)]
public void abc()
{
}
}
interface iii
{
void abc();
}

(o'piler +rror
a.cs(>-0,)/ error (%!5.6/ (onditional 'e'ber 3zzz.abc()3 cannot i'ple'ent interface 'e'ber
3iii.abc()3
243884994.doc 243 od 276

This particular attribute cannot be used on an interface function. Ask the guys who
designed the attribute what was on his mind when he placed the above restriction.

a.cs
class zzz
{
public int this[ref int i]
{
}
}


(o'piler +rror
a.cs(,-0.)/ error (%!5,0/ @nde4ers can3t have ref or out para'eters

It does not make any sense for an indexer to have parameters that can be ref or out and as
the parameter to an indexer is nothing but an index of an array. Normally the parameter to
an indexer would be a number only which cannot be changed. Also, if we used a variable,
changing its value in an indexer would lead to confusion all around.

a.cs
[zzz]
class zzz {
}

(o'piler +rror
a.cs(0-.)/ error (%!505/ 3zzz3/ is not an attribute class

An attribute must be a class derived from System.Attribute. This condition must be adhered
to at all costs. This is because the system can call lots of functions from the System.Atribute
class and we can override the functions if we want to change the behavior of the attribute.

a.cs
[444]
public class zzz {
[&&&]
int i;
}
abstract class &&& / %&ste'.:ttribute
{
}
class 444 / &&&
{
}

(o'piler +rror
a.cs(>-.)/ error (%!58,/ (annot appl& attribute class 3&&&3 because it is abstract
An abstract class cannot get a job anywhere as it cannot be used anywhere. Such a class is
incomplete and unless completed it cannot be of any help to society. Thus, the class yyy, an
abstract class, cannot participate as an attribute but class xxx can as it is not abstract. xxx
must implements all the abstract functions of class yyy.

a.cs
public class zzz
{
public static void Main()
{
243884994.doc 241 od 276
int i abc;
}
public static int abc()
{
return 0;
}
}

(o'piler +rror
a.cs(8-6)/ error (%!58>/ Method 3zzz.abc()3 referenced without parentheses

Function names have no great signifcance in the C# programming language. We only use
them to execute a function after specifying the name and () brackets with parameters within
them.

a.cs
public class zzz {
public static public void Main()
{
}
}

(o'piler +rror
a.cs(,-08)/ error (%0!!>/ Cuplicate 3public3 'odifier

Two of the same is what the compiler hates. Here we are not allowed to specify the same
modifer twice. Hey we meant no harm but who is listening. They do not have to be
physically together on the line for the error to be fagged.

a.cs
public class zzz {
public static void Main()
{
ob;ect Wp;
}
}

(o'piler +rror
a.cs(8-7)/ error (%0!!8/ @ndirection to 'anaged t&pe is not valid

A * signifes a pointer and the managed types like object etc cannot be accessed through a
pointer. Pointers were created for being used by the value types.

a.cs
public class zzz
{
public int i
{
set {}
set {}
}
}

(o'piler +rror
a.cs(5-8)/ error (%0!!1/ 2ropert& accessor alread& defined

You cannot say or do the same thing twice. As we are creating the same set accessor twice,
which one should the compiler take heed of. It chooses neither and gives you a simple error.
243884994.doc 242 od 276

a.cs
public class zzz {
int i
{
aa {}
}
}
(o'piler +rror
a.cs(>-0)/ error (%0!0>/ : get or set accessor e4pected

A property can only contain a get and a set.. We cannot place any arbitrary function name
within a property.

a.cs
public class zzz
{
public zzz () /
{
}
}

(o'piler +rror
a.cs(>-0)/ error (%0!07/ Ke&word this or base e4pected

After the name of the constructor, we have complete control over which constructors to call
though it is optional. If we specify a :, then we have to use base or this to call the
appropriate constructor. If we leave it blank, all hell breaks loose and we get an error.

a.cs
class zzz
{
}
}

(o'piler +rror
a.cs(>-0)/ error (%0!../ =&pe or na'espace definition- or endOofOfile e4pected

An extra brace } added by mistake does not result in a simple English like error. Instead, we
get end of fle expected. Where is the neighbourhood going these days?

a.cs
\vi;a&

(o'piler +rror
a.cs(0-.)/ error (%0!.>/ 2reprocessor directive e4pected
Anything after the # or pound sign can only be the name of a preprocessor symbol and not
anything else. As there are very few preprocessor symbols, we do not have much of a choice
here.

a.cs
\if true BW

WB
\endif

(o'piler +rror
a.cs(0-0!)/ error (%0!.8/ %ingleOline co''ent or endOofOline e4pected
243884994.doc 243 od 276

After a preprocessor directive, we can only use the single line comments and not the
multiple line comments.

a.cs
\if true .
\endif

(o'piler +rror
a.cs(0-0!)/ error (%0!.8/ %ingleOline co''ent or endOofOline e4pected

Any other preprocessor error also results in a CS1025. May be as the preprocessor has lost
its importance in C# compared to C, they did not have separate error messages for the
preprocessor and used CS1025 as a catch all for all errors.

a.cs
\if ( a b
\endif

(o'piler +rror
a.cs(0-0,)/ error (%0!.5/ ) e4pected

However, some common errors like closing a ) bracket are caught by the compiler.


a.cs
\endif

(o'piler +rror
a.cs(.-.)/ error (%0!.7/ Hne4pected preprocessor directive

When you catch the compiler with its pants down it gives you error CS1028. Here the
compiler saw an #endif with no matching #if. Instead of sending us to the loony bin, it gave
us the above error. Hey, relax, mistakes always happen!

a.cs
BW

(o'piler +rror
a.cs(0-0)/ error (%0!,8/ +ndOofOfile found- 3WB3 e4pected

All good things in life must come to an end even though we do not want them to. In this
case, we have a start of a multi-line comment but no end of multi line comment in sight.

a.cs
public class zzz
{
public static void Main()
{
string b L9hell;
}
}

(o'piler +rror
a.cs(8-0.)/ error (%0!,6/ Hnter'inated string literal

243884994.doc 244 od 276
All strings have to end with " as they start of with a ". If the @ sign is not around, you will
receive another error. Like in life end what you start.

a.cs
BW WB \define aa


(o'piler +rror
a.cs(0-1)/ error (%0!>!/ 2reprocessor directives 'ust appear as first nonOwhitespace character
on a line

We all like coming frst in life and it is the same with a preprocessor directive. We cannot
place it anywhere else, it has to be the frst character of a line. The number of spaces or
white spaces are counted as valid characters and like life ignores you and me, spaces are
ignored by the compiler.

a.cs
public class zzz
{
public stRu!!50tic void Main()
{
}
}

(o'piler +rror
a.cs(,-.!)/ error (%0806/ @nvalid toAen 3void3 in class- struct- or interface

A Unicode character set begins with a \u and a number, which the Unicode character is
known by. A small a is known by the number 97 or 61 in hex. We are not allowed to use the
Unicode characters in keywords. Maybe it brings in more work for the compiler.

a.cs
public class zzz
{
public static void Main()
{
abc(ref 0!);
}
public static void abc(ref int i)
{
}
}

(o'piler +rror
a.cs(8-6)/ error (%080!/ : ref or out argu'ent 'ust be an lvalue
A ref or an out must be a lvalue. An lvalue is nothing but a variable that represents some
memory. A property is not an lvalue as it is a function and does not stand for a memory
location. A ref and out parameter require a memory location.

a.cs
class zzz / &&&
{
public static void Main ()
{
base.; ,;
}
}
class &&&
243884994.doc 242 od 276
{
public static int ;;
}

(o'piler +rror
a.cs(8-0)/ error (%0800/ Ke&word base is not available in a static 'ethod

Static is like the odd kid on the block. It cannot perform a lot more than a non-static
member. It has, for example, no access to the keyword base with which it can access
members of the base class. The reason being that static means one and only one and this
and base are used to access multiple instance values.

a.cs
class zzz / &&&
{
int A base.;;
}
class &&&
{
public static int ;0!;
}

(o'piler +rror
a.cs(,-6)/ error (%080./ Ke&word base is not available in the current conte4t
We are not allowed to use the keyword base outside of a function, property or a constructor.
You can consider it as an extra parameter to a function. It represents the class we are
derived from.

a.cs
class zzz

(o'piler +rror
a.cs(,->)/ error (%080>/ { e4pected

Whenever we use keywords like class, namespace, the compiler checks for an open the
brace. If this character is omitted, then it stops in its tracks and immediately before you
can say 'halleluiah' it will throw up an error!

a.cs
class zzz
{
abc()
{
}
}

(o'piler +rror
a.cs(,-0)/ error (%08.!/ (lass- struct- or interface 'ethod 'ust have a return t&pe

A method can be placed in any of the following three places, a class, struct or an interface.
There are no assumptions made by the compiler. All functions have to return a value and
even if they do not, we have to explicitly specify that they do not. In the above case, the
function abc does not return any value and the compiler does not guess or assume a return
value for it.

a.cs
class zzz / &&&[]
243884994.doc 246 od 276
{
}
class &&&
{
}
(o'piler +rror
a.cs(0-07)/ error (%08.0/ @nvalid base t&pe

Whenever we use an invalid character in the name of the base class, we are confronted with
the above error. If we use the wrong name of a class, we are given a CS0234 error. Thus,
only an ill-formed base class name causes the above error.

a.cs
class zzz
{
public static void Main()
{
void i;
}
}

(o'piler +rror
a.cs(8-0)/ error (%08>1/ Ke&word 3void3 cannot be used in this conte4t

A void data type can only be used with a function. It denotes absence of a return value. A
variable cannot represent absence or no value. Thus, no data type can represent a void or
no value. For these reasons, the compiler has reserved one error message explicitly for it.

a.cs
class zzz
{
public static void Main()
{
8 $ [ $ throw $ void;
}
}

(o'piler +rror
a.cs(8-8)/ error (%08.8/ @nvalid e4pression ter' 3[3
a.cs(8-6)/ error (%08.8/ @nvalid e4pression ter' 3throw3
a.cs(8-01)/ error (%08.8/ @nvalid e4pression ter' 3void3

You are not allowed to write whatever you want and wherever you want especially in an
expression. We are not allowed to use reserved words indiscriminately anywhere we like.
Thus, words like void are a no-no in expressions. The compiler reserves error number
CS1457 for your sloppiness.

a.cs
class zzz
{
public static void Main()
{
zzz a new zzz;
}
}

(o'piler +rror
a.cs(8-05)/ error (%08.5/ : new e4pression re<uires () or [] after t&pe

243884994.doc 247 od 276
The compiler is extremely unforgiving. If we forget even the () after new, it screams its head
of at us. Whenever we call new, we are executing a constructor, which may or may not be
passed parameters. That is the rationale behind the () brackets after new. Even though we
have no parameters to be passed to the constructor, we have to use the new syntax. Ditto for
[].

a.cs
class zzz {
public static void Main()
{
zzz a(,);
}
}

(o'piler +rror
a.cs(8-5)/ error (%08.7/ +4pected ; or (cannot specif& constructor argu'ents in declaration)

In languages like C++, to create an object that looks like zzz, either new was used or the
class itself as in zzz a(3). This would create an object like zzz and call the constructor with
one int parameter. The parameter would be given he value of three.

C# treats value types and objects in the same manner. As most of us come from the C++
school of writing code, to prevent any such errors, the above error check has been
introduced. If you have a historical perspective of life, everything gets much clearly. One
bone we pick with the world is that nobody teaches history anymore. Those who do not
learn from history are forced to relive its mistakes, right?

a.cs
\line 9abc9

(o'piler +rror
a.cs(0-1)/ error (%0815/ =he line nu'ber specified for \line directive is 'issing or invalid

The #line directive requires by divine law two parameters, the new line number and the fle
name. Stating only one is not a valid option.

a.cs
\line 0! abc

(o'piler +rror
a.cs(0-0!)/ error (%0817/ Iilena'e- singleOline co''ent or endOofOline e4pected

The #line is an extremely sensitive animal. It needs a number followed by double quotations
marks and maybe a single line comment. Anything else will result in an error. Seen anyone
more stubborn in life?

a.cs
class zzz {
sealed override public void abc() {
}
}

(o'piler +rror
a.cs(.-.6)/ error (%!008/ 3zzz.abc()3/ no suitable 'ethod found to override
243884994.doc 248 od 276
The documentation clearly states that sealed can be used with a function within a class. A
sealed function as per the documentation can only be used with an override method.
Remove the override modifer and the error displayed is as follows:

a.cs(2,20): error CS0238: 'zzz.abc()' cannot be sealed because it is not an override

The concept of sealed for a function would prevent change to a single function only and not
to an entire class.

)ompiler ptions

When we run the C# compiler as

>(sc a.cs

Microsoft (J) Xisual (\ (o'piler Xersion 1.!!.6!,! [(#J version 0.!!...!>..0]
(op&right (() Microsoft (orp .!!!. :ll rights reserved.

We will frst see the Microsoft logo displayed. Seeing it all the time hurts the eye. Thus
Microsoft gives you a compiler option /nologo that will suppress the logo which would
otherwise get displayed.

>(sc a.cs Bnologo

We always run the compiler with the /nologo option. Nothing great here!

>(sc a.cs Bnostdlib

(o'piler +rror
error (%!807/ =he predefined t&pe 3%&ste'.?&te3 is not defined or i'ported

The entire System namespace has to be stored someplace and as of today it is stored in the
dll mscorlib.dll. This dll is searched and loaded always by the compiler thus the net efect is
that the System namespace is always available to us.
With the option of /nostdlib, the compiler is told to bypass this dll, mscorlib.dll, As the
System namespace is now unavailable to the compiler, an error is reported. For reasons left
unknown to us, the compiler frst requires System.Byte from the System namespace.

a.cs
class zzz
{
public static void Main()
{
}
}
na'espace %&ste'
{
struct ?&te
{
}
}

(o'piler +rror
error (%!807/ =he predefined t&pe 3%&ste'.@nt053 is not defined or i'ported

243884994.doc 249 od 276
Now that we have created a dummy, System.Byte structure the compiler goes away happy
but comes back with another request. It needs the System.Int16 structure. Can't it demand
everything in one go?

a.cs
class zzz
{
BBpublic static void Main()
BB{
BB}
}
na'espace %&ste'
{
struct ?&te
{
}
struct @nt05
{
}
struct @nt,.
{
}
}

On entering int16, the compiler asks for Int32. All this leads to one conclusion, if you don't
load the System namespace, then you have to write code for all the entities yourself. We
tried adding many more structures but eventually gave up as the list was endless. Rather
have the System namespace loaded from the dll, than key in all the structures ourselves.

a.cs
class zzz
{
public int this[]
{
}
}

(o'piler +rror
a.cs(,-01)/ error (%0880/ @nde4ers 'ust have at least one para'eter

An indexer is to be used as an array and hence must have, at the bare minimum, one
parameter to do its job. Else, we get an error.

a.cs
public class zzz
{
public static void Main()
{
int i;
}
}

(o'piler +rror
a.cs(8-8)/ warning (%!057/ =he variable 3i3 is declared but never used

The compiler produces a large number of warnings. Some feel that they are too old for
anyone to preach them. For them, the compiler can be commanded to stop generating
warning messages. This option is called /nowarn.
243884994.doc 223 od 276

>csc a.cs Bnowarn

Gives us the following error message:

(o'piler +rror
fatal error (%.!!5/ (o''andOline s&nta4 error/ Missing 3/"te4t>3 for 3Bnowarn3 switch

/nowarn is not enough, we need to specify the error number that is to be suppressed. On
giving the option as /nowarn:168, the compiler will not display warning message 168, the
one it displayed earlier. Multiple warning message numbers are to be separated by a comma
but there is no option available to suppress all of them.

a.cs
public class zzz
{
public static void Main()
{
int i;
&&& a new &&&();
}
}

b.cs
public class &&&
{
}

>csc W.cs

We are allowed to use wildcards with the compiler. The above command inturn gets
rewritten as csc a.cs b.cs.

>csc Brecurse/zzzRW.cs

The /recurse option lets you compile all .cs fles in zzz and its child directories recursively.

>csc Breference/'.dll;'0.dll a.cs

The /reference option lets you look into .dll fles for the code of classes. If you have more
than one dll fle to look into, then we need to separate them with a semicolon.

a.cs
public class zzz {
public static void Main()
{
int i;
}
}

(o'piler +rror
a.cs(8-8)/ error (%!057/ =he variable 3i3 is declared but never used

Run the command as

>csc a.cs Bwarnaserror

243884994.doc 221 od 276
The /warnaserror does a very simple thing. It converts all warnings into errors. This is
advisable as a policy because the compiler gives you a warning for a defnite purpose. If a
program compiles without any warnings, chances are good that it will not blow up in your
face ever.
>(sc a0.cs

(o'piler +rror
error (%.!!0/ %ource file 3a0.cs3 could not be found
fatal error (%.!!7/ Fo inputs specified

We get two errors instead of one. The frst error occurs because there is no fle called a1.cs
in the current directory. The second error arises as the only source fle we gave the compiler
was non-existent and hence the compiler was left with no input source fles.

>(sc a.cs a.cs

(o'piler )arning
warning (%.!!./ %ource file 3a.cs3 specified 'ultiple ti'es

If we give the same source fle name a.cs multiple times, we only get a warning. The compiler
ignores this repetition. You can give the same name as many times as you like and all of
them will be ignored

Create a fle z.rep as follows

z.rep
Bnologo a.cs b.cs

Run the compiler as

>csc Lz.rep

Anything after an @ sign in the command line is called a response fle. This fle contains
compiler options and source fle names. This just relieves you from writing the options and
flenames on the command line.

Run the command as

>csc Lz.rep Lz.rep

(o'piler +rror
error (%.!!,/ Jesponse file 3(/RcsharpRz.rep3 included 'ultiple ti'es

Even response fles cannot be mentioned multiple times.

>csc a.cs Br/

(o'piler +rror
error (%.!!8/ Missing file specification for 3Br/3 co''andOline option

With the /r option, the dll name is to be specifed. In case you forget the dll name, you pay
for it with an error.

>csc a.cs BA/

(o'piler +rror
243884994.doc 222 od 276
fatal error (%.!!1/ Hnrecognized co''andOline option/ 3BA/3

The compiler does not understand any option that begins with k:. Thus, the error is
reported. Looks like the command line options are not too many at all.

>csc a.cs La0.rep

(o'piler +rror
fatal error (%.!00/ Hnable to open response file 3(/RcsharpRa0.rep3

We asked the compiler to open a response fle that does not exist in the csharpbook sub-
directory. Hence, CS2011 error number is reported

a.cs
public class zzz
{
public static void Main()
{
}
}

b.cs
class &&&
{
hell
}

>csc Bbugreport/a.t4t a.cs b.cs
Microsoft (J) Xisual (\ (o'piler Xersion 1.!!.6!,! [(#J version 0.!!...!>..0]
(op&right (() Microsoft (orp .!!!. :ll rights reserved.


(o'piler +rror
b.cs(>-0)/ error (%0806/ @nvalid toAen 3}3 in class- struct- or interface 'e'ber declaration

: file is being created with infor'ation needed to reproduce &our co'piler proble'- including
the contents of all source code files.

2lease describe the co'piler proble' (press +nter twice to finish)/

Cescribe what &ou thinA should have happened (press +nter twice to finish)/

To create a bugreport, the compiler asks you to describe the problem that you have faced
and then press enter twice. At this point, we entered vijay.


The next question that pops up is for the solution to the problem. Here we key in sonal.

The contents of a.txt are displayed as follows.

a.t4t
\\\ (\ (o'piler Cefect Jeport- created !7B.6B!0 06/,,/06
\\\ (o'piler version/ 1.!!.6.8>
\\\ .F+= co''on language runti'e version/ v0.!..60>
\\\ *perating %&ste'/ )indows F= 8.!..068
\\\ Hser Fa'e/ :d'inistrator
\\\ (o'piler co''and line
csc Br/:ccessibilit&.dll Br/Microsoft.Xsa.dll Br/%&ste'.(onfiguration.@nstall.dll
Br/%&ste'.Cata.dll Br/%&ste'.Cesign.dll Br/%&ste'.Cirector&%ervices.dll Br/%&ste'.dll
243884994.doc 223 od 276
Br/%&ste'.Crawing.Cesign.dll Br/%&ste'.Crawing.dll Br/%&ste'.+nterprise%ervices.dll
Br/%&ste'.Manage'ent.dll Br/%&ste'.Messaging.dll Br/%&ste'.Junti'e.Je'oting.dll
Br/%&ste'.Junti'e.%erialization.Ior'atters.%oap.dll Br/%&ste'.%ecurit&.dll
Br/%&ste'.%ervice2rocess.dll Br/%&ste'.)eb.dll Br/%&ste'.)eb.Jegular+4pressions.dll
Br/%&ste'.)eb.%ervices.dll Br/%&ste'.)indows.Ior's.Cll Br/%&ste'.PM#.dll Bbugreport/a.t4t
a.cs b.cs
\\\ %ource file/ 3(/RcsharpbooARa.cs3
public class zzz
{
public static void Main()
{
}
}
\\\ %ource file/ 3(/RcsharpbooARb.cs3
class &&&
{
hell
}
\\\ (o'piler output
b.cs(>-0)/ error (%0806/ @nvalid toAen 3}3 in class- struct- or interface 'e'ber declaration
\\\ Hser description
vi;a&

\\\ Hser suggested correct behavior
sonal

Enter the following commands to understand the next error message.

>attrib $r a.t4t
>csc Bbugreport/a.t4t a.cs b.cs

(o'piler +rror
error (%.!0./ (an3t open 3a.t4t3 for writing

The attrib command with +r option will make the fle a.txt as read-only. As the compiler
could not write to it, it generates the above error. The compiler would rather gives us an
error than change the attribute. The above set of instructions demonstrated the need for
error number CS2012.

>csc Btarget/librar& a.cs Bbaseaddress/ui

(o'piler +rror
error (%.!0,/ @nvalid i'age base nu'ber 3ui3


Whenever we build a dll we can specify where the dll is to be loaded in memory. This is done
using the /baseaddress option which demands a number in hex. As we didn't fulfl its
needs, we are returned an error. The baseaddress is always specifed by default, if we do not
specify one.

>csc a.e4e

(o'piler +rror
error (%.!08/ 3(/RcsharpRa.e4e3 is a binar& file instead of a source code file
error (%08!>/ %ource file 3(/RcsharpRa.e4e3 could not be opened (3Hnspecified error 3)

We need to give the compiler a source fle consisting of a program and not a binary fle. The
second error is a natural corollary to the frst as it could not open the fle. These two errors
go hand and glove with each other.
243884994.doc 224 od 276

>csc a.cs Bcodepage/76!

(o'piler +rror
fatal error (%.!05/ (ode page 376!3 is invalid or not installed

A codepage has to do with diferent languages and your machine has to have the particular
code page installed on it before you can use it.

>csc Bnologo Btarget/librar& B'ain/zzz a.cs

(o'piler +rror
fatal error (%.!01/ (annot specif& B'ain if building a 'odule or librar&

Nobody likes contradictions. A library or a dll is a very diferent animal than an exe fle. You
can have either one or the other never both. We are frst ask the compiler to build a dll and
then specify the class that contains the entrypoint function Main. As main is valid only in
an exe fle, the error is reported


The next error message in sequence CS2018 points to a fle called cscmsgs.dll for all the
error messages. This fle does not exist on our computer.

>csc Bnologo Btarget/;;; a.cs

(o'piler +rror
fatal error (%.!06/ @nvalid target t&pe for Btarget/ 'ust specif& 3e4e3- 3wine4e3- 3librar&3- or
3'odule3

We can only build four types of output fles with this version of the compiler. We asked the
compiler to build the wrong type of target, hence the error.

(o'piler +rror
fatal error (%.!.0/ Iile na'e
3aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaa.cs3 is too long or invalid
fatal error (%.!!7/ Fo inputs specified

There is a variable/MACRO called MAX_PATH which has a value of 260. If our fle name
exceeds this size we will get an error CS2021. The compiler was asked to compile a fle with
a very long name, since the name was too long, the error gets reported. We have gone to
extents to display every error!

a.cs
[asse'bl&/%&ste'.(#%(o'pliant(true)]
public class zzz
{
internal void abc(QQarglist)
{
}
public void p<r(QQarglist)
{
}
}
243884994.doc 222 od 276

(o'piler +rror
a.cs(1-0,)/ error (%,!!!/ Methods with variable argu'ents are not (#%Oco'pliant

CLS stands for Common Language Specifcation and is the thingy that makes sure that a
class created in Cobol can have a function added in Perl. Also it can be a base class for a
class in Visual Basic and be fnally be called in C# or ASP.Net. To make sure that all our
code is CLS compliant, the above attribute shown above is to be applied to the class. Only
those functions that are not private or public can accept a variable number of arguments.
Thus, the abc function being internal does not give an error whereas pqr does.

a.cs
[asse'bl&/%&ste'.(#%(o'pliant(true)]
public class zzz
{
internal void abc(ushort i)
{
}
public void p<r(ushort i)
{
}
}

(o'piler +rror
a.cs(1-01)/ error (%,!!0/ :rgu'ent t&pe 3ushort3 is not (#%Oco'pliant

The ushort data type is not CLS compliant which means that all languages that are .NET
compliant do not have to support it. Like above, the rule applies only to private or public
members.

a.cs
[asse'bl&/%&ste'.(#%(o'pliant(true)]
public class zzz {
public ushort p<r()
{
return !;
}
}
(o'piler +rror
a.cs(>-7)/ error (%,!!./ Jeturn t&pe of 3zzz.p<r()3 is not (#%Oco'pliant

The return type cannot be a ushort in a CLS compliant class. In other words, the ushort
data type cannot be used anywhere.

a.cs
[asse'bl&/%&ste'.(#%(o'pliant(true)]
public class zzz
{
public void p<r()
{
ushort ;;
}
public ushort i;
}

(o'piler +rror
a.cs(5-7)/ warning (%!057/ =he variable 3;3 is declared but never used
a.cs(7-08)/ error (%,!!,/ =&pe of 3zzz.i3 is not (#%Oco'pliant

243884994.doc 226 od 276
We need to slightly modify the above statement. Variables created on the stack or local
variables cannot be marked with an access modifer. Thus, public instance variables cannot
be declared as ushort whereas local since they are not visible outside the function.

a.cs
[asse'bl&/%&ste'.(#%(o'pliant(true)]
public class zzz {
public int a0;
public int :0;
}

(o'piler +rror
a.cs(8-0.)/ error (%,!!8/ @dentifier 3zzz.:03 differing onl& in case is not (#%Oco'pliant

The C# programming language may be case sensitive but CLS languages may not be. Thus
when we write code which is CLS complaint, we make sure we are also case insensitive.

a.cs
[asse'bl&/ %&ste'.(#%(o'pliant(true)]
public class zzz
{
public int Qa ;
}

(o'piler +rror
a.cs(>-0.)/ error (%,!!7/ @dentifier 3zzz.Qa3 is not (#%Oco'pliant

There may be millions of programming languages out there, which some day will be .NET
compatible. Some of them may have a special meaning for the underscore character. Thus,
the CLS forbids an underscore in the name of a variable.

a.cs
[asse'bl&/%&ste'.(#%(o'pliant(true)]
public interface iii
{
[%&ste'.(#%(o'pliant(false)]
void abc();
}

(o'piler +rror
a.cs(8-1)/ error (%,!0!/ 3iii.abc()3/ (#%Oco'pliant interfaces 'ust have (#%Oco'pliant 'e'bers

It does not make sense for the entire interface to be CLS compliant other than one function
in it. Even if a single member is not CLS compliant, then the entire interface becomes CLS
non-compliant.

a.cs
[asse'bl&/ %&ste'.(#%(o'pliant(true)]
public abstract class iii
{
[%&ste'.(#%(o'pliant(false)]
public abstract int abc();
}

(o'piler +rror
a.cs(8-.0)/ error (%,!00/ 3iii.abc()3/ non (#%Oco'pliant 'e'bers cannot be abstract

243884994.doc 227 od 276
Programming languages the world over may not have all the sophistication as in C#. The
CLS specifcation is a simpler and easier C#. Therefore a large number of features in C# are
not implemented in the CLS. Remember, C# is today arguably one of the world's most
powerful languages. One of the feature supported in C# and not a CLS compliant is the
notion of abstract. Hence, the above error. The CLS is a subset of the C# language
specifcation.

a.cs
['odule/%&ste'.(#%(o'pliant(true)]
public class zzz
{
public static void Main() {
}
}

(o'piler +rror
a.cs(0-6)/ warning (%,!0./ ]ou 'ust specif& the (#%(o'pliant attribute on the asse'bl&- not the
'odule- to enable (#% co'pliance checAing

The error message says it all. You cannot specify the above attribute on a module.

a.cs
[asse'bl&/%&ste'.(#%(o'pliant(false)]
public class iii
{
[%&ste'.(#%(o'pliant(true)]
public void abc()
{
}
}

(o'piler +rror
a.cs(>-.)/ error (%,!0>/ 3iii.abc()3 cannot be 'arAed as (#% co'pliant because the asse'bl& is
not 'arAed as co'pliant
Just as we had some lines earlier, we cannot have a class which is CLS compliant and a
single member within it which is non-CLS compliant. One rotten apple spoils the entire lot.

a.cs
[asse'bl&/%&ste'.(#%(o'pliant(true)]
public class zzz / %&ste'.:ttribute
{
public zzz(int[] i)
{
}
}

(o'piler +rror
a.cs(.-0>)/ error (%,!08/ 3zzz3 has no accessible constructors which use onl& (#% co'pliant t&pes

At the outset, we need one constructor that is CLS complaint. Here we have only one
construct but it is not CLS complaint as arrays are not part of the allowed data types in a
method call.

a.cs
class zzz
{
public static void Main()
{
243884994.doc 228 od 276
%&ste'.(ollections.:rra&#ist a new %&ste'.(ollections.:rra&#ist();
%&ste'.(onsole.)rite#ine(a.@te'[!]);
BB (onsole.)rite#ine(9Iirst +le'ent is {!}9- a.[!]);
}
}

(o'piler +rror
a.cs(5-.5)/ error (%!001/ 3%&ste'.(ollections.:rra&#ist3 does not contain a definition for 3@te'3

When using indexed properties like those in the ArrayList, we do not have to complicate life.
The ArrayList object is called a. To access its member, in place of a.Item[0], a simple a.[0]
will do.

Nothing complicated here. We do not have to use the Item specifer at all.

We can never ever show you a an error check for CS0174 as it occurs whenever we recompile
System.Object. We do not have the source code for it and hence we cannot compile it. It is
the only class that does not have any base class at all!

a.cs
public class zzz
{
public static void Main ()
{
locA (0)
{
}
}
}

(o'piler +rror
a.cs(8-0)/ error (%!078/ 3int3 is not a reference t&pe as re<uired b& the locA state'ent

The lock statement requires a reference type for it to perform its job. The parameter cannot
be an int or a value type, hence the error. The lock statement works only on reference types.

We hate functions as they are so boring. They insist that we always pass them the same
number of parameters. There is a way out to make sure that functions are allowed to receive
a variable number of parameters. To accomplish this task, the keyword __arglist is
incorporated. N number of parameters can be given then.

__arglist used as a parameter name in the function then contains a reference to the variable
number of arguments. To access the values, we create an object that looks like ArgIterator
with the __arglist as a parameter. The GetRemainingCount function within this object
returns the count of parameters and each parameter is then retrieved using the GetNextArg
member of the ArgIterator.


The values can be converted to an int using __refvalue. If we do not then we will get a
System.TypedReference instead of the actual value. This is how we can pass a variable
number of parameters to a function.

a.cs
public class zzz {
public static void abc (int a)
{
243884994.doc 229 od 276
ob;ect o QQarglist;
}
}

(o'piler +rror
a.cs(8-0.)/ error (%!06!/ =he QQarglist construct is valid onl& within a variable argu'ent 'ethod

You can only use a __arglist construct in methods, which will accept a variable number of
arguments.

a.cs
public class zzz
{
public unsafe static void Main ()
{
int Wi null;
int ; !;
; i[0-.];
}
}

>csc a.cs Bunsafe

(o'piler +rror
a.cs(1-8)/ error (%!065/ : pointer 'ust be inde4ed b& onl& one value

The array brackets are only a notation. They internally get converted into a pointer. Thus i[2]
internally becomes *(i+2). Thus, we can never have multiple indices for a pointer. Unlike C,
at one level the C# programming language and its faithful agents do not understand the
meaning of an array.

a.cs
public class zzz
{
unsafe public static void Main ()
{
zzzWW a null;
foreach (zzz 4 in a)
{
}
}
}

(o'piler +rror
a.cs(5-0)/ error (%0816/ foreach state'ent cannot operate on variables of t&pe 33 because 33 does
not contain a definition for 3Net+nu'erator3- or it is inaccessible

The variable a is a pointer to a pointer to a zzz. This means that variable a contains the
address of another variable which in turn contains the address of an object that looks like
zzz.

The foreach statement does not understand objects completely. It requires certain functions
to be present in the object for it to successfully enumerate all of them.

a.cs
public class zzz
{
unsafe public int i
{
243884994.doc 263 od 276
unsafe set {}
}
}

(o'piler +rror
a.cs(8-0)/ error (%05!6/ Modifiers cannot be placed on propert& or event accessor declarations


A property is superior to its get and set accessors. If the property is made unsafe then it is
common knowledge that the accessors will also be unsafe. We have realized by now that the
compiler does not like us stating the obvious and hence adding the keyword unsafe on the
set accessor will bring about the above error.

a.cs
public class zzz
{
unsafe public static void Main()
{
&&& a new &&&();
&&& Wb;
b &a;
}
}
class &&&
{
}

(o'piler +rror
a.cs(5-8)/ error (%!.!7/ (annot taAe the address or size of a variable of a 'anaged t&pe (3&&&3)
a.cs(1-8)/ error (%!.!7/ (annot taAe the address or size of a variable of a 'anaged t&pe (3&&&3)

A two-in-one error. A combo at last! The concept of pointers existed in the C programming
language, which did not understand classes let alone managed types. Thus, pointers are
best left alone to deal with values types and not managed types. We cannot fgure out their
addresses in memory nor their size. A lot about managed types are best handled internally
by the compiler and are beyond our understanding. Like the secret services, we cannot know
everything.

a.cs
public class zzz {
unsafe public static void Main()
{
fi4ed (int i)
{
}
}
}

(o'piler +rror
a.cs(>-7)/ error (%!.!6/ =he t&pe of locals declared in a fi4ed state'ent 'ust be a pointer t&pe.
a.cs(>-0.)/ error (%!.0!/ ]ou 'ust provide an initializer in a fi4ed or using state'ent declaration

The garbage collector, if allowed to move objects in memory, would make the pointer values
meaningless. Hence, we use the fxed statement to pin down a variable in memory while the
code is executing. We mark these objects as special using the statement fxed as we want to
prevent the garbage collector from moving objects holding addresses around in memory. It is
obvious that addresses can only be stored by a pointer and not by any other variable. A fxed
keyword is applicable only on pointers.
243884994.doc 261 od 276

a.cs
public class zzz
{
unsafe public static void Main()
{
fi4ed (intW i)
{
}
}
}

(o'piler +rror
a.cs(8-0,)/ error (%!.0!/ ]ou 'ust provide an initializer in a fi4ed state'ent declaration

Assuming we do use a pointer in a fxed statement, our problems are not yet over. We have
to initialize this pointer to an object or any other value including null as in int *i = null. We
are not allowed to take the address of a managed object anywhere else except in a fxed
statement. The {} brackets is the range for which the garbage collector promises not moving
around as the objects has been pinned.

a.cs
public class zzz
{
unsafe public void Main()
{
int ; !;
int Wi &(; $ .!);
}
}

(o'piler +rror
a.cs(5-0!)/ error (%!.00/ (annot taAe the address of the given e4pression
a.cs(5-0!)/ error (%!.0./ ]ou can onl& taAe the address of unfi4ed e4pression inside of a fi4ed
state'ent initializer

Not everything in life has an address. Only variables have an address. The expression j+20
has no address as it does not represent a unique memory location. Thus, we can only give i=
and use the & in front of entities that are stored in memory.

a.cs
public class &&& {
public int i !;
}
public class zzz
{
unsafe public static void Main() {
&&& a new &&&();
int W; &a.i;
}
}

(o'piler +rror
a.cs(7-0!)/ error (%!.0./ ]ou can onl& taAe the address of unfi4ed e4pression inside of a fi4ed
state'ent initializer

There are certain rules to be followed in an unsafe context. The object a is an instance of
class yyy and this makes it a managed object. Its address can only be taken in a fxed
statement and not anywhere else.
243884994.doc 262 od 276
a.cs
public class &&&
{
public int i !;
}
public class zzz
{
unsafe public static void Main()
{
&&& a new &&&();
fi4ed ( int W; null)
{
; &a.i;
}
}
}

(o'piler +rror
a.cs(0!-0>)/ error (%!.0,/ ]ou do not need to use the fi4ed state'ent to taAe the address of an
alread& fi4ed e4pression
a.cs(0.-8)/ error (%!.0./ ]ou can onl& taAe the address of unfi4ed e4pression inside of a fi4ed
state'ent initializer
a.cs(0.-0)/ error (%05!>/ (annot assign to 3;3 because it is readOonl&

We explicitly said & inside a fxed statement and not in the range of the fxed. The only way
out of a nagging compiler is as follows.

a.cs
public class &&& {
public int i !;
}
public class zzz
{
unsafe public static void Main()
{
&&& a new &&&();
fi4ed ( int W; &a.i)
{
}
}
}
No errors at last. A huge sigh of relief!

a.cs
public class zzz
{
unsafe public static void Main()
{
int i !;
fi4ed (int W; &i)
{
}
}
}

(o'piler +rror
a.cs(5-01)/ error (%!.0,/ ]ou do not need to use the fi4ed state'ent to taAe the address of an
alread& fi4ed e4pression

243884994.doc 263 od 276
Value types like an int are created on the stack. Their positions in memory cannot change
for the entire duration of the function. At the end of the function they die. The garbage
collector cannot move them around in memory at all for optimizing your code to make it run
faster. Thus, if you take the address of a local variable in a fxed statement, the compiler
throws an error at you for doing the obvious.

a.cs
public class zzz {
public void abc(para's int[] a- QQarglist)
{
}
}

(o'piler +rror
a.cs(.-01)/ error (%!.,0/ : para's or QQarglist para'eter 'ust be the last para'eter in a for'al
para'eter list

We cannot have life the way we want. The params and __arglist allow us to invoke a function
with a variable number of arguments. We have to choose on one of them. We cannot choose
both as the compiler will not know where one starts and the other gets over. This cannot be
called a restriction ever.

a.cs
delegate int ddd(QQarglist);

(o'piler +rror
a.cs(0-0>)/ error (%!.,8/ QQarglist is not allowed in delegates

We are allowed to use the params keyword in a delegate if we want it to accept a multiple
number of arguments. We cannot implement the same concept by using __arglist instead as
it has fallen out of favor with the compiler. It is a simple rule that the compiler likes, so do
we.

a.cs
[%&ste'.Junti'e.@nterop%ervices.(o'@'port]
public class a
{
}

(o'piler +rror
a.cs(0-81)/ error (%!865/ =he Nuid attribute 'ust be specified with the (o'@'port attribute

The attribute called ComImport lets you specify a class as an ActiveX object. Every ActiveX
object is known by a 16 byte or a 128 bit number called its GUID or Globally Unique
Identifer. We forget to specify this 16 byte number and hence our ActiveX object was only
known by its name and not its number, which is an error.

a.cs
using %&ste'.Junti'e.@nterop%ervices;
[(o'@'port- Nuid(9vi;a&9)]public class a
{
}

(o'piler +rror
a.cs(.-0,)/ error (%!5>1/ +rror e'itting
3%&ste'.Junti'e.@nterop%ervices.Nuid:ttribute3 attribute OO 3@ncorrect uuid for'at.3

243884994.doc 264 od 276
No programming language as of now can represent a 16 byte number directly. We have a
structure called GUID which represents this 16 byte number. We have to represent this 16
byte number is a certain way which looks like: Guid("1234abcd-a234-abcd-1111-
678987654678"). Since the program does not follow the rules, an error is reported

a.cs
public class zzz
{
[%&ste'.Junti'e.@nterop%ervices.Cll@'port(9H%+J,..C##9)]
e4tern int Message?o4(int a);
}

(o'piler +rror
a.cs(,-.)/ error (%!5!0/ =he Cll@'port attribute 'ust be specified on a 'ethod 'arAed 3static3
and 3e4tern3.

The DllImport attribute informs the compiler that the code of the function MessageBox is in
a dll fle called user32.dll. As we have not created this function and the code too is not
present in the fle, we have to signify that it is static and extern. We need both modifers, one
alone will not sufce.

a.cs
using %&ste'.Junti'e.@nterop%ervices;
public class zzz
{
[Cll@'port(9Aernel,..dll9)]
public static e4tern int abc();
}

The attributes DllImport applies to static and extern functions only.

a.cs
using %&ste';
using %&ste'.Junti'e.(o'piler%ervices;
public class &&&
{
[@nde4erFa'e(9vi;a&9)]
public int this[int i]
{
set { }
}
}
public class zzz / &&&
{
[@nde4erFa'e(9vi;a&9)]
public override int this[int i] {
set { }
}
}

(o'piler +rror
a.cs(0.-.)/ error (%!5!6/ (annot set the @nde4erFa'e attribute on an inde4er 'arAed override

You can be extremely selective the code that should access the attributes. The attribute
called IndexerName cannot be used on an indexer, which is override but can be used on the
indexer in class yyy.

a.cs
using %&ste';
243884994.doc 262 od 276
public class &&& / :ttribute {
int i;
}
[&&&(i!)]
class zzz {
}

(o'piler +rror
a.cs(8-5)/ error (%!501/ 3i3 is not a valid na'ed attribute argu'ent. Fa'ed attribute argu'ents
'ust be fields which are not readonl&- static or const- or properties with a set accessor which
are not static.

There is no way that we can use the private members of an attribute class. The member i is
private in class yyy and hence can only be used if we use the access modifer public in front
of it. The error also tells us about the named arguments that can be used. At times, these
error messages are not bad guys and can be helpful at times.
a.cs
using %&ste';
public class &&&
{
[*bsolete(9vi;a& 'uAhi9- false)]
public static void abc()
{
}
}
class zzz
{
public static void Main()
{
&&&.abc();
}
}

(o'piler )arning
a.cs(0,-0)/ warning (%!507/ 3&&&.abc()3 is obsolete/ 3vi;a& 'uAhi3

We have placed the Obsolete tag in front of the function abc. The warning message vijay
mukhi is only displayed if we access function abc. Remember it is not an error but an aid to
inform the user not to use a certain function as they maybe discontinued in the future
versions.

Change the false in the attribute to true as follows

[*bsolete(9vi;a& 'uAhi9- true)]

(o'piler +rror
a.cs(0,-0)/ error (%!506/ 3&&&.abc()3 is obsolete/ 3vi;a& 'uAhi3

All that happens is that the warning message gets converted to an error. You decide on how
serious it is if we use a certain function which may get obsolete very soon.

a.cs
public class zzz {
%&ste'.=&pedJeference i;
}
(o'piler +rror
a.cs(.-0)/ error (%!50!/ Iield or propert& cannot be of t&pe
3%&ste'.=&pedJeference3
243884994.doc 266 od 276

The class TypedReference is special and cannot be used to create a feld but can be used as
parameters to a method.

a.cs
public class zzz
{
%&ste'.=&pedJeference[] i new %&ste'.=&pedJeference[0];
}

(o'piler +rror
a.cs(,-.,)/ error (%!500/ :rra& ele'ents cannot be of t&pe %&ste'.=&pedJeference

An array type cannot also be a TypedReference.

a.cs
public class zzz
{
static e4tern public void abc();
}

(o'piler +rror
a.cs(,-.1)/ warning (%!5.5/ Method- operator- or accessor 3zzz.abc()3 is 'arAed e4ternal and has
no attributes on it. (onsider adding a Cll@'port attribute to specif& the e4ternal i'ple'entation

An extern method should have an attribute of DllImport. This specifes the dll fle containing
the implementation of the function. We expected an error and got a warning instead. The
world was never a predictable place to stay in!

a.cs
using %&ste';
[*bsolete(9Xi;a&9-@s+rrorfalse)]
interface iii
{
}
public class zzz
{
public static void Main ()
{
ob;ect[] a t&peof(@0).Net(usto':ttributes();
(onsole.)rite#ine (((*bsolete:ttribute)a[!]).@s+rror);
}
}

(o'piler +rror
a.cs(.-06)/ error (%!5,./ 3@s+rror3 / Fa'ed attribute argu'ent can3t be a read onl& propert&

An attribute can have read only properties and they cannot be use as named arguments.
The above error takes place as IsError is a readonly property in the attribute obsolete and
we are trying to fetch its value in the WriteLine function.

a.cs
using %&ste';
using %&ste'.Junti'e.(o'piler%ervices;
public class zzz
{
[@nde4erFa'e(9vi;a& 'uAhi9)]
public int this[int ;]
{
243884994.doc 267 od 276
set {}
}
}

(o'piler +rror
a.cs(>-1)/ error (%!5,,/ argu'ent to @nde4erFa'e attribute 'ust be a valid identifier

The name attribute names the indexer. We gave an invalid name of vijay mukhi, it has a
space in it. Remove the space and the error goes away. The name must follow all the rules of
a valid identifer.

a.cs
using %&ste'.Junti'e.@nterop%ervices;
[%truct#a&out(#a&outKind.%e<uential)]
struct zzz
{
[Iield*ffset(>!)]
public int i;
}

(o'piler +rror
a.cs(8-.)/ error (%!5,5/ Iield*ffset attribute can onl& be placed on 'e'bers of t&pe 'arAed
with the %truct#a&out(#a&outKind.+4plicit)

The FieldOfset attribute specifes where each feld will appear in memory. We have a
complete control over the layout of the structure. The only condition is that we need to use
the LayoutKind as Explicit and not as Sequential.

a.cs
using %&ste'.Junti'e.@nterop%ervices;
[%truct#a&out(#a&outKind.+4plicit)]
struct zzz
{
[Iield*ffset(>!)]
public static int i;
}

(o'piler +rror
a.cs(8-.)/ error (%!5,1/ =he Iield*ffset attribute not allowed on static or const fields

One more restriction on the attribute FieldOfset is that we cannot use it on static or const
felds as the compiler would like to decide where to place them in memory.

a.cs
[abc/vi;a&]
class zzz
{
}

(o'piler +rror
a.cs(0-5)/ error (%!587/ 3abc3 is not a valid attribute location
Here, abc is any arbitrary name that we have chosen, hence the error is reported.

a.cs
using %&ste';
[:ttributeHsage(:ttribute=argets.:ll)]
public class zzz
{
}
243884994.doc 268 od 276

(o'piler +rror
a.cs(.-.)/ error (%!5>0/ 3:ttributeHsage3 / attribute is onl& valid on classes derived fro'
%&ste'.:ttribute

The class AttributeUsage is used to inform the compiler where the attribute class can be
used. Thus the class following must be derived from System.Attribute to qualify as an
attribute class. If you do not follow the above rules, you will get saddled with the above error.

a.cs
[%&ste'.Jeflection.CefaultMe'ber:ttribute(9via;&9)]
class zzz
{
public int this[int i]
{
set {}
}
}

(o'piler +rror
a.cs(0-.)/ error (%!5>5/ (annot specif& the CefaultMe'ber attribute on t&pe containing an
inde4er

Any class that is tagged with a DefaultMemberAttribute cannot be used with a class that
contains an indexer. Strange are the ways of the world at frst sight.

Note: Xml warnings are only generated if you use the /doc: option along with the compiler
i.e. csc a.cs /doc:a.xml


a.cs
public class zzz
{
public static zzz operator B (zzz a0- zzz a.)
{
return null;
}
BBB "seealso cref9zzz.operatorL9B>
public static void Main()
{
}
}

(o'piler )arning
a.cs(0-0>)/ warning (%0860/ Missing PM# co''ent for publicl& visible t&pe or 'e'ber 3zzz3
a.cs(,-06)/ warning (%0860/ Missing PM# co''ent for publicl& visible t&pe or 'e'ber
3zzz.operator B(zzz- zzz)3
a.cs(1-,,)/ warning (%0!!,/ %&nta4 error- 393 e4pected
a.cs(1-,.)/ warning (%0!,6/ Hnter'inated string literal
a.cs(1-,.)/ warning (%0!,1/ *verloadable operator e4pected
a.cs(1-.!)/ warning (%087>/ PM# co''ent on 3zzz.Main()3 has s&ntacticall& incorrect cref
attribute 3zzz.operatorL3

The compiler found an invalid link with ///. The operator statement was wrong as it did not
specify the correct operator.

>csc a.cs Bnologo Bresource/a.b'p

243884994.doc 269 od 276
(o'piler +rror
error (%0855/ +rror reading resource file 3(/RcsharpRa.b'p3 OO 3=he s&ste' cannot find the file
specified. 3

This error takes place when the compiler cannot fnd the specifed flename.

>csc a.cs Bnologo Bresource/a.b'p-aa Bresource/b.b'p-aa

(o'piler +rror
error (%08!7/ Jesource identifier 3aa3 has alread& been used in this asse'bl&

We copied two fles a.bmp and b.bmp in our current subdirectory. Using the /resource
option, we can specify a name for the resource. We gave the same name and hence we
received an error.

a.cs
\if aa
\endif
\if Uaa
\endif
\if (aa)
\endif
\if true
\endif
\if false
\endif
\if 0
\endif
\if Zaa
\endif
\if W
\endif

(o'piler +rror
a.cs(00-8)/ error (%0801/ @nvalid preprocessor e4pression
a.cs(0,-8)/ error (%0801/ @nvalid preprocessor e4pression
a.cs(08-8)/ error (%0801/ @nvalid preprocessor e4pression

The last three preprocessors directives gave us an error as we cannot use a number, the ~
sign or a * to the #if. True and false are ok and so are () etc.

>csc a.cs Bwarn/0!!

(o'piler +rror
error (%06!!/ )arning level 'ust be in the range !O>

There are only fve warning levels, starting from zero. Depending upon the warning number
used, we will see certain types of warnings. The rest will be suppressed.

>csc a.cs Bwarn/! Bwarnaserror
(o'piler +rror
error (%06!0/ (onflicting options specified/ )arning level !; =reat warnings as errors

/warn:0 turns the warnings of and /warnaserror treats warnings as errors. The compiler
thinks we are drunk, hence it tries to clarify things about these two mutually exclusive
options.
>csc a.cs Breference/RcharpbooA

243884994.doc 273 od 276
(o'piler +rror
error (%!!!5/ Metadata file 3Rcharp3 could not be found

We have to specify the name of a fle not a subdirectory.

>csc a.cs Breference/R

(o'piler +rror
error (%08>0/ @nvalid include option/ 3R3 OO cannot include directories

We have to give some compiler options an actual fle name. Names of sub-directories are not
allowed.

a.cs
class zzz {
}

>csc a.cs B'ain/zzz

(o'piler +rror
a.cs(0-1)/ error (%0887/ 3zzz3 does not have a suitable Main 'ethod

The /main option specifes which class contains the method main. If we had two .cs fles
and each contained the Main function, the /main option informs the compiler about the fle
it should consider having the entry point.

>csc a.cs B'ain/zzzz


(o'piler +rror
error (%0888/ (ould not find 3zzzz3 specified for Main 'ethod

The /main option has to be supplied with the name of a class that exists in the .cs fles.
Common sense.

class zzz {
}

(o'piler +rror
a.cs(0-1)/ error (%0887/ 3zzz3 does not have a suitable Main 'ethod

The /main compiler option specifes the class containing the function Main. We we so
absent-minded that we forget to write one in the class zzz, hence the compiler comes back
with the relevant error.

a.cs
\line 0. 9abccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc9

(o'piler +rror
a.cs(0-00)/ error (%085!/ Iilena'e specified for \line is too long

The name of the fle in the #line directive cannot exceed 256 characters. We exceeded the
limit.

>csc Bnologo Bwin,.res/a.res Bwin,.icon/a.ico
243884994.doc 271 od 276

(o'piler +rror
error (%0858/ (onflicting options specified/ )in,. resource file; )in,. icon

A res fle stores various resources like images, sound fles, menus, icons etc. in a compiled
form. An .ico fle can be used to store an icon exclusively too. The above two options hate
each other and thus cannot be given together.
>attrib $r a.4'l
>csc Bnologo Bdoc/a.4'l a.cs

(o'piler +rror
error (%0856/ +rror generating PM# docu'entation file 3a.4'l3 (3:ccess is denied. 3)

We made the fle a.xml read only and as the /doc option could not write to the fle we receive
the above error.

a.cs
BB the following line generates (%081!
BBB "su''ar&> returns true if " 8 "Bsu''ar&>
public class zzz
{
}

(o'piler )arning
a.cs(.-,.)/ warning (%081!/ PM# co''ent on 3zzz3 has badl& for'ed PM# OO 3)hitespace is not
allowed at this location.

XML has its own set of rules. We are not allowed to use the < sign in the /// tags. We have
to use &lt; instead. There are a large number of ways to create ill formed XML and any badly
formed XML will generate the above error. Thus, even in the comments someone is checking
what you are doing.

a.cs
public class zzz
{
BBB "para' na'e3aa3>Hsed to indicate status."Bpara'>
BBB "para' na'e3aa3>Hsed to indicate status."Bpara'>
public static void Main (string [] aa)
{
}
}

(o'piler )arning
a.cs(>-07)/ warning (%0810/ PM# co''ent on 3zzz.Main(string[])3 has a duplicate para' tag for
3aa3


Nobody likes being told to do something twice. Even in a /// tag! The param denotes a
parameter to a function, so, we cannot have two parameters with the same name in a
method.

a.cs
public class zzz {
BBB "para' na'e3aaa3>Hsed to indicate status."Bpara'>
public static void Main (string [] aa)
{
}
}

243884994.doc 272 od 276
(o'piler )arning
a.cs(,-07)/ warning (%081./ PM# co''ent on 3zzz.Main(string[])3 has a para' tag for 3aaa3- but
there is no para'eter b& that na'e
a.cs(>-.!)/ warning (%081,/ 2ara'eter 3aa3 has no 'atching para' tag in PM# co''ent (but
other para'eters do)

The frst warning is very clear. Our function Main accepts a parameter called aa and in the
XML tag we have given the name as aaa. The second warning is that the function name has
a parameter called aa and we do not have a param tag it.

a.cs
BBB "e4ception cref9%&ste'.(onsole.)rite#in9>:n e4ception class."Be4ception>
class zzz / %&ste'.+4ception
{
}

(o'piler )arning
a.cs(0-..)/ warning (%081>/ PM# co''ent on 3zzz3 has cref attribute 3%&ste'.(onsole.)rite#in3
that could not be found

The compiler checks every move of yours. The cref attribute needs a valid method name
WriteLine is not spelt right as the last e is missing, hence the error.

a.cs
BBB "seealso cref9abc(i)9B>
public class zzz
{
public void abc(int i)
{
}
public void abc(char i)
{
}
}

(o'piler )arning
a.cs(0-.!)/ warning (%087!/ @nvalid t&pe for para'eter 303 in PM# co''ent cref attribute
a.cs(0-.!)/ warning (%081>/ PM# co''ent on 3zzz3 has cref attribute 3abc(i)3 that could not be
found

The cref also takes a data type for the parameters to a function, not the name of the
parameter. Here, we could specify a int or a char but not the name of the parameter i.

a.cs
public class zzz
{
public static e4plicit operator int(zzz f)
{
return !;
}
}
BBB "seealso cref9zzz.e4plicit operator intt(zzz)9B>
public class &&&
{
}

(o'piler )arning
a.cs(0-0>)/ warning (%0860/ Missing PM# co''ent for publicl& visible t&pe or 'e'ber 3zzz3
a.cs(,-08)/ warning (%0860/ Missing PM# co''ent for publicl& visible t&pe or 'e'ber
3zzz.e4plicit operator int(zzz)3
243884994.doc 273 od 276
a.cs(7-.!)/ warning (%0870/ @nvalid return t&pe in PM# co''ent cref attribute


The name of the operator is int and not intt. Be careful while typing the name.

a.cs
public class zzz
{
BBB "re'arAs> (alled in "see cref9zzz//Main9B> "Bre'arAs>
public static void Main()
{
}
}

(o'piler )arning
a.cs(,->!)/ warning (%0!,5/ ( or . e4pected
a.cs(,-,1)/ warning (%087>/ PM# co''ent on 3zzz.Main()3 has s&ntacticall& incorrect cref
attribute 3zzz//Main3

The XML tag must have everything prim and proper. The separator between the class name
and function name is a . and not a ::. Have to be careful what we do in front of a computer!

(sc Bnologo a.cs Bwin,.res/a.4'l

(o'piler +rror
error (%087,/ 3(/RcsharpRa.4'l3 is not a valid )in,. resource file

A res fle has a certain fle format that is documented on the Microsoft web site. We gave a
text fle instead. The compiler checks the validity of a res fle before reading it.

a.cs
public class zzz
{
unsafe public static void Main() {
int Wp stacAalloc int (,!);
}
}

(o'piler +rror
a.cs(>-.8)/ error (%0818/ : stacAalloc e4pression re<uires [] after t&pe
The stackalloc keyword is used to allocate memory for us. It has the array syntax and not
the function syntax. The [] brackets are mandatory for this function and not the ().

>csc Bnologo a.cs BJ/a.ico

(o'piler +rror
fatal error (%!!!6/ Metadata file 3(/RcsharpRa.ico3 could not be opened OO 3Iile is corrupt3

An .dll fle has a predefned fle format called the PE or Portable Executable fle format. A
.ico fle format is very diferent from the PE format. We can not mix up fle formats in
compiler options.

a.cs
public class zzz
{
public static void Main()
{
}
243884994.doc 274 od 276
}

>csc a.cs

a..cs
public class &&& / zzz
{
public static void Main()
{
}
}

csc a..cs Br/a.e4e

a,.cs
class 444 / &&&
{
public static void Main()
{
}
}

csc a,.cs Br/a..e4e gives the error

(o'piler +rror
a..e4e/ error (%!!00/ Jeferenced class 3&&&3 has base class or interface 3zzz3 defined in an
asse'bl& that is not referenced. ]ou 'ust add a reference to asse'bl& 3a3.

We come across a slight roadblock. The fle a.exe contains a class zzz and a2.exe contains
class yyy. When we compile a3.cs with the /r: option alongwith a2.exe as it contains class
yyy, we get an error. A.exe should have also been stated as it contains the code for zzz which
yyy is derived from. Thus to remove the error we should restate the command as

>csc a,.cs Br/a..e4e;a.e4e

a.cs
class &&&
{
}
public class zzz
{
public &&& &;
}

(o'piler +rror
a.cs(5-0.)/ error (%!!8./ @nconsistent accessibilit&/ field t&pe 3&&&3 is less accessible than field
3zzz.&3

As a re-revision, we are not allowed to use the class yyy in class zzz as a public variable
because the class is declared internal by default. The member y cannot be more accessible
than the class yyy that is internal. As y is declared public, the error pops up. Change it to a
less accessible modifer like private or internal and the error simply vanishes. If the class
yyy is made public then none of the above problems would arise. You can easily become less
important than you really are but cannot aspire to greater heights.

>attrib $r a.e4e

>csc a.cs
243884994.doc 272 od 276

(o'piler +rror
error (%!!05/ (ould not write to output file 3a.e4e3 OO 3:ccess is denied. 3

As the fle a.exe is marked as read only, when the compiler tries to write the new output fle
it came back with the above operating system error.

The compiler command given as

>csc Bout/a.e4e a.cs Bincre'ental

creates a fle called a.exe.incr in the same sub-directory. This fle contains information to
speed up the compilation process. So far we have been using only one or two fles that carry
our code. In a real-life situation we will have over a thousand fles. Each time we run the
compiler we do not need all the fles to be recompiled but only those functions that have
changed since the last time we compiled. This speeds up the entire process as most fles will
not be recompiled again. This information on the new changes in a function must be stored
at someplace. That fle is a .incr fle. We will display the frst line of this fle.

a.e4e.incr
@ncre'ental ?uild Cata for (\ (o'piler OO this file stores data to enable fast rebuilds.

>attrib $r a.e4e.incr
>csc Bout/a.e4e a.cs Bincre'ental

Like before we made the fle cool.incr read only.

error (%!!,./ (ould not open incre'ental build file 3(/RcsharpRa.e4e.incr3 for writing

>csc a.cs Bdebug

This creates a fle called a.pdb, which is approx 12K large. This fle contains debugging
information. Whenever programs do not work, a debugger is used to point out the exact
location of the error and the reasons behind it. A computer detectives frst tool is debugger
and no self respecting computer professional travels without one always.

>attrib $r a.pdb

(o'piler +rror
fatal error (%!!>./ Hne4pected error creating debug infor'ation file 3a.2C?3 OO 3a.pdb/ :ccess is
denied.

>csc Btarget/'odule Bout/a.dll a.cs

The /target option builds a dll fle named a.dll and not an executable. A dll traditionally
contains code and cannot be executed at all.

>csc b.cs BJ/a.dll

Produces

(o'piler +rror
error (%08!6/ Jeferenced file 3(/RcsharpRa.dll3 is not an asse'bl&; use 3Badd'odule3 option
instead

243884994.doc 276 od 276
The error goes away when the command is written

>(sc b.cs Badd'odule/a.dll

Any fle specifed in a dll has to internally carry an assembly manifest. Using /addmodule,
we want to add only the metadata information for a.dll to the assembly of the current
program. It does not add the assembly of a.dll to the assembly of the current program.

>csc Btarget/librar& a.cs

Produces a.dll

(sc Badd'odule/a.dll b.cs

Produces the following error.

(o'piler +rror
error (%08>./ 3(/RcsharpRa.dll3 cannot be added to this asse'bl& because it alread& is an
asse'bl&; use 3BJ3 option instead

Anything produced by /target can only be used with a /reference and not by /addmodule.

We have an extremely versatile compiler with us. If we run as

>csc Btarget/e4e a.cs Btarget/'odule b.cs

It will create only one fle, i.e. an exe fle

>csc Btarget/e4e a.cs Btarget/e4e b.cs

Produces

(o'piler +rror
error (%.!.!/ *nl& the first set of input files can build a target other than 3'odule3

)he first @tar"et can 'e either an e$e or a #ine$e or a i'rary. )he remainin" fies can ony
have ony a @tar"etAmodue. )his is more for a muti0out!ut com!iation.

Vous aimerez peut-être aussi