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


 

Comments (8)

  1. Kelly Summerlin says:

    I’ve looked at the IL for enums before. I’ve wondered if it would be possible to detect the Flags attribute and automatically generate the bit mask values (0x1, 0x2, 0x4, etc) automatically by the compiler. Could that be a possibility in the future?

  2. Blake says:

    You write "Enums derive from System.Enum — wow, structs can have base class other than ValueType."

    Really? That’s what the pseudo C# that you’ve reversed out of the IL suggests, but that’s not remotely true at either the C# language or the CLR implementation level. What exactly are you getting at here?

  3. John Tobler says:

    If you do not have it already, check out Lutz Roeder’s Reflector: http://www.aisto.com/roeder/dotnet/. The current version not only gives you a Class Browser but also makes it more convenient to use ILDASM and, even better, contains a nice decompiler. Recommended!

  4. Dunc says:

    "structs can have base class other than ValueType" — well, with C# they can’t!

  5. The foreach can be also interesting, if you write this:

    ArrayList list = new ArrayList();

    foreach(int x in list)
    {
    // do nothing
    }

    the generated code will be like this:

    ArrayList list1;
    int num1;
    IEnumerator enumerator1;
    IDisposable disposable1;

    list1 = new ArrayList();

    enumerator1 = list1.GetEnumerator();
    try
    {
    while (enumerator1.MoveNext())
    {
    num1 = ((int) enumerator1.Current);

    }
    return;
    }
    finally
    {
    disposable1 = (enumerator1 as IDisposable);
    if (disposable1 != null)
    {
    disposable1.Dispose();

    }

  6. Howard Hoffman says:

    Re the Enum discussion, point #5, I’ve often wondered if there is a performance difference between ‘static readonly’ and ‘const’.

    I’ve figured that the static would be preferred because there would only be 1 "instance" of the value, where a ‘const’ would take up data space in each class instance.

    Given:

    public class Foo
    {
    private static readonly string MyStaticSprocName = "Items_SelectByPrice";
    private const string MyInstanceSprocName = "Items_SelectByPriceAndQuantityOnHand";

    }

    For usage like this, or somewhere you want C++ #define-like semantics (think resource.h, for example), is there a best practice recommendation?

    BTW – Like your tips…please keep them coming…