.NET Interoperability at a Glance 2 – Managed Code Interoperation

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

See more Interoperability examples here.


Contents of this article:

  • Contents
  • Read also
  • Overview
  • Introduction
  • Forms of Interop
  • Managed Code Interop
  • Summary
  • Where to go next

Read also

More from this series:


In the previous article, you learnt what interoperability is and how it relates to the .NET Framework. In this article, we’re going to talk about the first form of interoperability, the Managed Code Interop. In the next article, we’ll talk about the other forms.


So, to understand Interoperation well in the .NET Framework, you must see it in action. In this article, we’ll talk about the first form of .NET interoperability and see how to implement it using the available tools.

Just a reminder, Interoperability is the process of communication between two separate systems. In .NET Interop, the first system is always the .NET Framework; the other system might be any other technology.

Forms of Interop

Interoperability in .NET Framework has two forms:

  • Managed Code Interoperability
  • Unmanaged Code Interoperability

Next, we have a short discussion of each of the forms.

Managed Code Interop

This was one of the main goals of .NET Framework. Managed Code Interoperability means that you can easily communicate with any other .NET assembly no matter what language used to build that assembly.

Not surprisingly, because of the nature of .NET Framework and its runtime engine (the CLR,) .NET code supposed to be called Managed Code, while any other code is  unmanaged.

To see this in action, let’s try this:

  1. Create a new Console application in the language you like (C#/VB.NET.)
  2. Add a new Class Library project to the solution and choose another language other than the one used in the first project.
  3. In the Class Library project, add the following code (choose the suitable project):
    // C#
    public static class Hello
        public static string SayHello(string name)
            return "Hello, " + name;
    ' VB.NET
    Public Module Hello
        Public Function SayHello(ByVal name As String) As String
            Return "Hello, " & name
        End Function
    End Module
  4. Now, go back to the Console application. Our goal is to call the function we have added to the other project. To do this, you must first add a reference to the library in the first project. Right-click the Console project in Solution Explorer and choose Add Reference to open the Add Reference dialog (figure 1.) Go to the Projects tab and select the class library project to add it.

    Figure 1 - Add Reference to a friend project

    Figure 1 - Add Reference to a friend project

  5. Now you can add the following code to the Console application to call the SayHello() function of the class library.
    // C#
    static void Main()
        Console.WriteLine(ClassLibrary1.Hello.SayHello("Mohammad Elsheimy"));
    ' VB.NET
    Sub Main()
        Console.WriteLine(ClassLibrary1.Hello.SayHello("Mohammad Elsheimy"))
    End Sub

How this happened? How could we use the VB.NET module in C# which is not available there (or the C#’s static class in VB which is not available there too)?

Not just that, but you can inherit C# classes from VB.NET (and vice versa) and do all you like as if both were created using the same language. The secret behind this is the Common Intermediate Language (CIL.)

When you compile your project, the compiler actually doesn’t convert your C#/VB.NET code to instructions in the Machine language. Rather, it converts your code to another language of the .NET Framework, which is the Common Intermediate Language. The Common Intermediate Language, or simply CIL, is the main language of .NET Framework which inherits all the functionalities of the framework, and which all other .NET languages when compiled are converted to it.

So, the CIL fits as a middle layer between your .NET language and the machine language. When you compile your project, the compiler converts your code to CIL statements and emits them in assembly file. In runtime, the compiler reads the CIL from the assembly and converts them to machine-readable statements.

How CIL helps in interoperation? The communication between .NET assemblies is now done through the CIL of the assemblies. Therefore, you don’t need to be aware of structures and statements that are not available in your language since every statement for any .NET language has a counterpart in IL. For example, both the C# static class and VB.NET module convert to CIL static abstract class (see figure 2.)

Figure 2 - CIL and other .NET languages

Figure 2 - CIL and other .NET languages

Managed Interop is not restricted to C# and VB.NET only; it’s about all languages run inside the CLR (i.e. based on .NET Framework.)

If we have a sneak look at the CIL generated from the Hello class which is nearly the same from both VB.NET and C#, we would see the following code:

.class public abstract auto ansi sealed beforefieldinit ClassLibrary1.Hello
       extends [mscorlib]System.Object

    .method public hidebysig static string  SayHello(string name) cil managed
      // Code size       12 (0xc)
      .maxstack  8
      IL_0000:  ldstr      "Hello, "
      IL_0005:  ldarg.0
      IL_0006:  call       string [mscorlib]System.String::Concat(string,
      IL_000b:  ret
    } // end of method Hello::SayHello

} // end of class ClassLibrary1.Hello

On the other hand, this is the code generated from the Main function (which is also the same from VB.NET/C#):

.class public abstract auto ansi sealed beforefieldinit ConsoleApplication1.Program
       extends [mscorlib]System.Object

    .method private hidebysig static void  Main() cil managed
      .maxstack  8
      IL_0000:  ldstr      "Mohammad Elsheimy"
      IL_0005:  call       string [ClassLibrary1]ClassLibrary1.Hello::SayHello(string)
      IL_000a:  call       void [mscorlib]System.Console::WriteLine(string)
      IL_000f:  ret
    } // end of method Program::Main

} // end of class ConsoleApplication1.Program

You can use the ILDasm.exe tool to get the CIL code of an assembly. This tool is located in Program FilesMicrosoft SDKsWindows<version>bin.

Here comes a question, is there CIL developers? Could we write the CIL directly and build it into .NET assembly? Why we can’t find much (if not any) CIL developers? You can extract the answer from the CIL code itself. As you see, CIL is not so friendly and its statements are not so clear. Plus, if we could use common languages to generate the CIL, we we’d like to program in CIL directly? So it’s better to leave the CIL for the compiler.

Now, let’s see the other form of .NET interoperation, Unmanaged Code Interoperability.


So, the secret of Managed Code Interoperation falls in the Common Intermediate Language or CIL. When you compile your code, the compiler converts your C#/VB.NET (or any other .NET language) to CIL instructions and saves them in the assembly, and that’s the secret. The linking between .NET assemblies of different languages relies on the fact that the linking is actually done between CILs of the assemblies. The assembly code doesn’t (usually) have any clue about the language used to develop it. In the runtime, the compiler reads those instructions and converts them to machine instructions and execute them.

Next, we’ll talk about the other form of .NET Interoperation, it’s the interoperation with unmanaged code.

Where to go next

Read more about Interoperability here.

More from this series:

Manually performing a garbage collection!

I was asked whether it’s efficient to perform a manual garbage collection using GC.Collect() or not!

Actually, it’s better to just let the CLR handle it. However, there’re few circumstances where manually performing garbage collection is preferred, such as if some non-recurring event has just happened and this event is highly likely to have caused a lot of old objects to die.

More details are available in Rico Mariani’s…

Have a nice weekend!