Introduction to MSIL – Part 3 – Defining Types

In this installment of the MSIL series, I describe how types are defined.

Here is a minimal reference type called House. As I write this, we are looking for a house in British Columbia, so this was the first thing that came to mind.

.class Kerr.RealEstate.House
{
    .method public void .ctor()
    {
        .maxstack 1
       
        ldarg.0 // push "this" instance onto the stack
        call instance void [mscorlib]System.Object::.ctor()
       
        ret
    }
}

This is a very simple type. Note that you must declare a constructor for a concrete reference type. Unlike languages like C# and C++, the IL assembler will not generate a constructor for you automatically.

Types are defined using the .class directive followed by a type header. The class keyword is used instead of the more intuitive type for historical reasons. When you read class in MSIL source code, just think type. The type header consists of a number of type attributes followed by the name of the type you are defining. To define the equivalent of a C# static class in MSIL you can write the following.

.class abstract sealed Kerr.RealEstate.MortgageCalculator
{
    /* members */
}

abstract and sealed are the type attributes. An abstract type cannot be instantiated and a sealed type cannot have sub-types. There are attributes to control visibility, such as public and private. There are attributes to control field layout, such as auto and sequential. For a complete list of attributes please consult the CLI specification. Many attributes are applied automatically, which can save you a lot of typing. Fortunately these defaults are quite intuitive so you should become familiar with them quickly. As an example, extending the System.ValueType from the mscorlib assembly defines a value type. Since the CLI requires that value types be sealed, the IL assembler will automatically add this attribute for you.

The name of the type in the example above is Kerr.RealEstate.MortgageCalculator. The CLI does not recognize namespaces as a distinct concept. Rather the full type name is always used. The syntax shown above is only supported by the IL Assembler that ships with version 2.0 of the .NET Framework. If you are working with version 1.x then you need to group your namespace members with a .namespace directive, as shown below. Note that this syntax is also supported in version 2.0.

.namespace Kerr.RealEstate
{
    .class abstract sealed MortgageCalculator
    {
        /* members */
    }
}

Following the type name you have the option of specifying the base type. The extend keyword is used for this purpose. If no base type is specified, the IL assembler will add the extend clause to make the type inherit from the System.Object type from the mscorlib assembly, resulting in a reference type. And finally, the type header can provide a list of interfaces that the type and its descendants will implement and satisfy, becoming the interfaces of the type.

.class Kerr.RealEstate.RoomList
    extends [System.Windows.Forms]System.Windows.Forms.ListView
    implements Kerr.IView
{
    /* members */
}

In this example, the Kerr.RealEstate.RoomList type has System.Windows.Forms.ListView, defined in the System.Windows.Forms assembly, as its base type. Keep in mind that the CLI requires that every user-defined type extend exactly one other type. The RoomList type also implements the Kerr.IView interface type.

With this basic introduction to type definitions, you should now be able to start defining more interesting types. To define an interface, simply use the interface attribute in your type header. If you want a value type, known as a struct in C#, simply extend the System.ValueType type from the mscorlib assembly. Now you should be able to see why the .class directive is perhaps not the best name for it.

.class interface Kerr.IView
{
    /* members */
}

.class Kerr.RealEstate.HouseData
    extends [mscorlib]System.ValueType
{
    /* members */
}

Read part 4 now: Defining Type Members


© 2004 Kenny Kerr

 

7 Comments

  • This series has been very enlightening, although I have a question about namespaces. It seem like just assembling the first snippet failes on "Kerr.RealEstate.House". If you make it just "House", it works. You say that MSIL does not recognize namespaces as a distinct concept, so why does this matter? Is there something you need to do to declare the "Kerr.RealEstate" namespace?



    Brian

  • Sorry about that. I probably should have mentioned that I'm describing MSIL as of version 2.0 of the .NET Framework. I have updated this post to clarify this and answer your question as to how to get it to work with version 1.x

  • Kenny,



    I am really enjoying the series on MSIL. Can you poin us to some resources that you used in order to come to grips with MSIL?



    Thanks. Looking forward to part 4.



    James

  • Thanks James. Mostly my experience comes from hours pouring over the IL generated by different compilers to understand how they work and what kind of performance characteristics I can expect from my code. If you’re serious about MSIL, you should definitely take a look at the CLI specs. They tend to be quite long and verbose and that is why I am writing the MSIL series to give developers a good introduction to it without requiring hours pouring over specs with endless details that are only interesting to some (like me). Thanks for the feedback. I’m glad to see interest in this series. I will continue adding to it as long as readers stay interested. I’m planning to tackle some more advanced MSIL topics in the coming days and weeks.

  • For the sake of clarity, MSIL is an assembly language targeting the CLI. Compilers generate metadata, not IL. I just tend to think of it in terms of IL since it’s a simple way of expressing CLI constructs without favoring a popular programming language. In addition, the CLR team ensures that no features are added to the runtime that cannot be expressed in MSIL.

  • Thanks you!
    If I write code with only MSIL, the code has a more performance than writing with C#?

  • Kim: In general the performance will be comparable. Keep in mind that C# simply produces IL. So assuming the C# compiler generates the best possible code, the performance should be the same. If you’re concerned about performance you may want to consider the Visual C++ 2005 compiler which tends to produce slightly more efficient code due to more advanced optimizations, but the difference is negligible in most cases.

Comments have been disabled for this content.