Inside C#

One of the ways I explore the framework
is to look under the covers at what the compilers generate to see how the map to CLR
concepts. ILDASM is a great tool for
this. Here a few interesting examples
I thought I’d share. "urn:schemas-microsoft-com:office:office" />I
ILDASM’ed a C# program then translated (roughly) the IL back into C# for your reading
pleasure.

You
write:

public
enum Compression {

   Zip,
SuperZip, None

 }

The
C# compiler generates:

public
struct Compression : Enum {

   public
int value__;

   public
const Compression Zip = 0;

   public
const Compression SuperZip = 1;

   public
const Compression None = 2;

}

What
can we learn:

Well, I think we can learn a couple
of things.

1) Enums
are all structs. All rule that applies to structs will apply to enums as well

2) Enums
derive from System.Enum --- wow, structs can have base class other than ValueType.

3) The
underlying type of enums by default is int (could be any integer type)

4) Names
with “__” must be reserved for use by the compiler.

5) The
values are all consts… which means that
are burned into the call site – you can’t ever change them without recompiling all
the clients

6) The
values are all auto numbered for you based on the order in the source. Better
not reorder them in the source, see #5.

7) All
the values are of type Compression, this always the strong type checking in the compiler.

You
write:

public class Resource

{

   ~Resource()
{

      ...

  }

}

The
C# compiler generates:

public class Resource

{

   protected
override void Finalize() {

      try
{

         ...

      }

      finally
{

         base.Finalize();

      }

   }

}

What
can we learn:

1) destructor
syntax (The ~) is nothing more than an override of the finalize method off object. As
such we know it is not deterministic.

2) The
implementations of this method (represented by the “…”) is wrapped in a try..finaly
to be sure the base classes finalize method always get a chance to run even if you
throw an exception.

You
write:

using (Resource res = new
Resource()) {

   res.DoWork();

}

The
C# compiler generates:

Resource res = new Resource(...);

try {

   res.DoWork();

}

finally {

   if
(res != null)

((IDisposable)res).Dispose();

}

What
can we learn:

Res.Dispose() is guaranteed to get
called when the using statement is exited, normally of via an exception

You
write:

object x = ... ;

lock (x) {

   //
critical section

}

The
C# compiler generates:

System.Threading.Monitor.Enter(x);

try {

// critical section

}

finally {

System.Threading.Monitor.Exit(x);

}

What
can we learn:

1) Lock
just uses Monitor.Enter and Exit… reading those docs give you a good idea of what
is really happening here.

2) We
see our friend try..finally again. This time ensuring that the monitor is exited even
if an exception is thrown in the critical section