Hidden similarity between instance level methods in Python and C#

By | May 31, 2015

So I am brushing up on my Python skills because I want to improve upon my knowledge of scripting languages. I code in C# more often than not which is a statically typed language and I wish to expand my horizons by getting more serious about dynamically typed languages.

One thing that I noticed while going through some sample tutorials is that we define instance level methods using a reference to the instance object as the first argument using the conventional variable name of self.

class Talker(object):
    def greet(self, name):
        print 'Hello, %s!' % name

    def farewell(self, name):
        print 'Farewell, %s' % name

To some programmers (especially those from a .NET background) this may seem a bit cumbersome. However, if we define the equivalent class in C#:

public class Talker
{
    public void Greet(string name)
    {
        Console.WriteLine("Hello, {0}", name);
    }

    public void Farewell(string name)
    {
        Console.WriteLine("Farewell, {0}", name);
    }
}

The C# compiler will turn this C# code into CIL (Common Intermediate Language) code before it is eventually generated into native code (a couple steps later) for execution by the CPU.

For the Greet method, the following CIL code will be generated:

.method public hidebysig instance void  Greet(string name) cil managed
{
  // Code size       14 (0xe)
  // DRW: The maximum number of variables that may be pushed onto the stack 
  // at any given time during the execution of the method is 8.
  .maxstack  8
  IL_0000:  nop
  // DRW: Load a string onto the virtual execution stack
  IL_0001:  ldstr      "Hello, {0}"     
  // DRW: Load the variable "name" onto the stack (exists at index 1 of the method's arguments)
  IL_0006:  ldarg.1  
  // DRW: Call the WriteLine method from type Console                   
  IL_0007:  call       void [mscorlib]System.Console::WriteLine(string, object)
  IL_000c:  nop
  // DRW: Exit the method
  IL_000d:  ret
} // end of method Talker::Greet

For those who are unfamiliar will CIL code, I have commented the non-‘no op’ lines for you. The particular point of interest is that the “name” argument actually exists at index 1 of the method’s arguments – So the question is what exists at index 0? Well index 0 contains a reference to the current object (think C# keyword this). In fact, every non-static method receives a reference to the current object as an implicit additional parameter at index 0 of the incoming arguments.

So why is this significant? Well it means that the .NET Framework in a sense does require that for instance level methods that a reference to current object is passed into the method. This is much like the requirement in Python, however, Python just happens to be much more up front with us about it. Instead in the .NET Framework, it is done behind the scenes and is visible to us in the CIL code (although somewhat indirectly yet). Python is designed with the idea that explicit is better than implicit.

Note that for static methods in C# there is no implied first parameter as a reference to the current object in the CIL code. Likewise, in Python you can make a method implicitly a class level method by omitting the reference to the object instance as the first argument to the method, however, the practice is somewhat not recommended.

It is interesting to see a commonality between these vastly different languages where it is not immediately obvious that there is one.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.