Methods and MSIL Code


The final subject in this chapter on methods will be a discussion on MSIL code. MSIL is the low-level programming instruction set produced by all compilers that target the .NET Framework.

Looking at the MSIL code produced by the C# compiler in this chapter is important because we can see how the different types of methods are compiled into MSIL code, and also show the broader importance of MSIL methods that are covered in the remaining chapters. This insight will change the way you will think about the elegant and sometimes complex object-orientated C# code you produce.

To look at how simple methods are compiled into MSIL code, create the following program, which includes an example of the main types of methods. This program can also be created in Notepad and is saved in the source code as msil.cs:

    using System;    class MSILMethods    {      static void Main()      {        int result = 0;        int total;        int net = 0;        MethodExamples examples = new MethodExamples();        result = examples.ByValueMethod(7);        examples.ByRefMethod(ref result);        examples.OutputMethod(out total, result);        result = result + examples.OverloadMethod(total);        total = examples.OverloadMethod(result, total);        net = examples.ParamsMethod(result, total);        Console.WriteLine(net);      }    }    class MethodExamples    {      public int ByValueMethod(int a)      {        return a++;      }      public void ByRefMethod(ref int b)      {        b = b * 2;      }      public void OutputMethod(out int c, int d)      {        c = d / 4;      }      public int OverloadMethod(int e)      {        return e + 2;      }      public int OverloadMethod(int e, int f)      {        return e + f;      }      public int ParamsMethod(params int[] g)      {        int total = 0;        foreach(int num in g)        {          total = total + num;        }        return total + 1;      }    } 

Compile msil.cs and examine the MSIL code produced by the C# compiler using the utility ildasm:

    C:\Class Design\Ch03>ildasm msil.exe 

Once ildasm loads, first expand the MSIL methods node, select msil.exe, and click on View, and then Show Token Values. Now expand the msil node. The following screen should be displayed, which displays all of the members of the MSILMethods class:

click to expand

Double click on .ctor : void() and the following screen should be displayed:

click to expand

From the MSIL code emitted by the C# compiler we can make the following observations about the implicit constructor of the MSILMethods class:

  • We can tell from this that for the MSILMethods class, that constructor is actually a method from the .method declaration. Constructors are actually compiled into MSIL code as methods. This is important because, when considering the design and usage of constructors, we know that constructors are really methods, although quite specialized methods in terms of their purpose in class design.

  • The constructor method (.ctor) makes a call to the System.Object constructor. It is from this that we can tell the base class of our type. The MSILMethods class does not explicitly derive from any type within the original .cs file, but implicitly derives from System.Object. This is why a call is made to the System.Object constructor.

Close the .ctor window and double click the Main() method node. The following window should be displayed:

click to expand

From the Main() method, note the following:

  • We can tell from the MSIL code that Main() is implemented as .NET managed code through the cil managed flag in the method declaration.

  • Because we have created a console application, Main() is also the entry point to the program. We can tell this from the .entrypoint directive. This is what distinguishes .exe files from .dll files. All .exe files always have one method with the .entrypoint directive.

  • In the Invoking Methods section, we discussed how stack frames are created to allocate a fixed block of memory on the stack to be used for parameters and local variables. The .maxstack directive tells us the maximum number of stack-based variables our method needs to execute this method. In the example, we have four stack-based variables – total, result, and net, (which are all of type int), and example (a reference pointer to a stack-based object of type MethodExamples). The actual size of the Stack Frame is calculated by the C# compiler and emitted into the MSIL code. The value of the .maxstack directive is actually optimized to allow parts of the stack frame to be reused when variables are either no longer required, or go out of scope. The optimized calculation for .maxstack is based upon the maximum stack size the method requires to execute, which is not necessarily the number of variables within the method. The optimized calculation depends on a number of factors including variable type (int, bool, reference, and so on), maximum number of arguments passed to other methods, and variable scope.

Next, close the Main() window and expand the MethodExamples node. This node contains all of the members of the MethodExamples class:

click to expand

Double-click on the first OverloadMethod member and the following screen should be displayed:

click to expand

In between the .method directive and the public access modifier we can see a token tag of 06000007. The token tag uniquely identifies this member within the assembly. Now close this window and double-click on the second OverloadMethod member of MethodExamples. The following screen will be displayed:

click to expand

Here we can see that the second overload of OverloadMethod() has a different token tag 06000006. Because token tags are unique within an assembly, this means that overloaded methods are in fact compiled as completely separate methods and the token tag is actually used to resolve which overloaded method to call. We can prove that this resolution process is made at compile time by reexamining the Main() method of the MSILMethods class:

click to expand

You might need to scroll across the window to view the token tags called by Main().

By examining the MSIL code emitted by the C# compiler, we can deduce the following:

  • Class members, including overloaded members with the same name are implemented as separate members in MSIL code. The resolution of an overloaded member by comparing the method signature against the argument list is made by the C# compiler.

  • The C# compiler calculates the size of each stack frame required to invoke a method. This calculation is optimized to use the least amount of memory possible.

  • Constructors are also implemented as methods. The same is also true of properties and operators, which are discussed in the next chapter.




C# Class Design Handbook(c) Coding Effective Classes
C# Class Design Handbook: Coding Effective Classes
ISBN: 1590592573
EAN: 2147483647
Year: N/A
Pages: 90

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net