OSDotNet.003: CIL and .NET Assemblies — Intermediate Language, Metadata, and Manifests

Technical illustration of .NET assembly compilation, CIL instructions, metadata, and runtime execution displayed at a dark server-lab workstation.

Learning Objectives and Prerequisites

A compiled .NET application contains more than the original C# source. Common Intermediate Language (CIL), structured metadata and the assembly manifest help the runtime understand and execute a managed program. Learners will identify those components, distinguish CIL from native instructions, and build and inspect a sample assembly without changing production systems.

Review OSDotNet.001 — The .NET Platform and OSDotNet.002 — The CLR. A supported .NET SDK and a terminal are required for the optional build lab; the C# compiler and SDK lesson covers setup.

Elementary Overview

Imagine a labeled box containing written instructions, a parts list and an identity card. The instructions describe what to do, the parts list identifies what each item means, and the card identifies the box. A .NET assembly works similarly: CIL contains intermediate instructions, metadata describes types and members, and the manifest identifies the assembly and its dependencies.

A managed .dll commonly contains processor-independent code that a compatible runtime can translate into native instructions. This model supports C#, F# and other managed languages, but not every .NET deployment uses just-in-time compilation: Native AOT and other publishing techniques can generate native machine code ahead of execution. The standard managed assembly remains the central model for studying the .NET runtime.

Nick Chapsas, “Hello World in .NET written using IL code” (June 1, 2021), demonstrates constructing managed intermediate language directly.

From Source Code to Native Execution

A typical .NET compilation path consists of several different jobs. A language compiler checks C# source, emits CIL and metadata, and packages them in an assembly. The runtime loads that assembly and typically JIT-compiles invoked method bodies into instructions for the target CPU. The Microsoft managed-execution guide documents the relationship.

The following sequence is a conceptual model, not terminal output:

C# source → compiler → CIL + metadata → managed DLL → CLR/JIT → native CPU instructions

The original C# syntax is lost as source text in the ordinary binary output, but enough structured information remains for runtime loading and reflection. Compare the source-level concepts in the C# variables and types lesson and the C# strings lesson.

The CIL Instruction Set

CIL is a standardized, stack-oriented instruction set. Rather than requiring a specific processor register, many instructions work with an evaluation stack. A method can load a constant, call another method, branch, perform arithmetic or return. Terms such as IL and MSIL often refer to the same managed intermediate-language concept in developer tools.

The following three lines illustrate how a managed method might print a short string; actual output differs according to compiler, project and target framework:

ldstr "Hello, .NET!" call void [System.Console]System.Console::WriteLine(string) ret

Here ldstr loads a string reference, call invokes the referenced method, and ret returns. Real disassembly also includes method signatures, metadata references and offsets. The Microsoft ldstr documentation describes its use of metadata string tokens.

Four Typical Assembly Components

A static managed assembly normally has an assembly manifest, type metadata, CIL code and optional resources. The manifest provides assembly identity and references; metadata describes types, fields, methods, signatures and attributes; CIL contains method bodies; resources can hold text, images or other embedded information. The exact set of resources depends on the application.

The Microsoft assembly contents guide describes those elements. The manifest documentation identifies fields including name, version, culture, dependency references and, where used, public-key information. A strong name is an identity mechanism, not a substitute for evaluating whether binary code is trustworthy.

Editorial view of a .NET C# programming workstation.
Original .NET development-workstation illustration; technical binary-layout details are explained by Microsoft and the ECMA specification.

Metadata and Self-Describing Files

Metadata is binary information describing the types and members defined or referenced by a .NET component. Tables hold type definitions, method definitions, member references and assembly references; heaps store strings and other data. The runtime can use metadata when loading code, resolving calls, validating types and supporting reflection.

A metadata token identifies a table entry. For example, Microsoft documents 0x06000004 as a method-definition token referring to the fourth row of the MethodDef table. That number is an illustrative reference, not a token measured from the learner’s own compiled project. Read the self-describing components guide and the ECMA-335 Common Language Infrastructure standard for the formal structures.

A file extension alone is not proof of internal format. A managed DLL, a native DLL and a native application launcher can all be present in modern .NET workflows. Inspect the actual managed assembly rather than assuming that every executable contains CIL.

Assembly Names, Versions, and Dependencies

An assembly manifest acts like a catalog entry. The assembly name identifies its component, a four-part version can describe compatibility, and references help the runtime find dependent assemblies. The usual assembly-version notation is Major.Minor.Build.Revision. A marketing or product version may differ from the assembly version; neither number should be guessed from a file creation date.

Applications can depend on assemblies from libraries and packages. Modern .NET build output may also contain a runtime configuration file and a dependency manifest in JSON, which serve hosting and dependency purposes but are not themselves the CIL-containing assembly. The Microsoft assembly-versioning guide explains why assembly identity matters.

Practical Lab — Build and Inspect Your Own Assembly

Use a terminal in a disposable project directory. Confirm the installed SDK, create a new console project, and navigate into it. The commands are presented without fixed-width code boxes:

dotnet --list-sdks dotnet new console --name CilWorkshop cd CilWorkshop

Open the generated Program.cs file and replace its initial contents with a reflection example. The code asks the currently executing assembly for its own name, version and type count:

using System; using System.Reflection; Assembly assembly = Assembly.GetExecutingAssembly(); AssemblyName identity = assembly.GetName(); Console.WriteLine($"Assembly: {identity.Name}"); Console.WriteLine($"Version: {identity.Version}"); Console.WriteLine($"Defined types: {assembly.GetTypes().Length}");

Compile into a predictable folder and run the console application. The build is permitted to restore required SDK components; avoid using untrusted package feeds for an educational test:

dotnet build --output ./artifacts dotnet run

The expected name is CilWorkshop. The exact assembly version and count of defined types may vary with SDK and project settings; they are not fixed values asserted by the lesson. Observe the displayed metadata rather than interpreting a successful compilation as proof that every type and dependency is correctly configured.

Inspect the output directory for CilWorkshop.dll and companion runtime files. The DLL is the managed assembly; the .deps.json and .runtimeconfig.json files support dependency and runtime selection. A separate native application host may be generated as well, depending on the selected build platform.

Optional IL Disassembly with ILSpy

The Windows Visual Studio developer tools include an IL Disassembler known as ildasm. The open-source ILSpy project supplies an assembly browser and the cross-platform ilspycmd tool. As documented by the current project, the latest pointer package requires .NET SDK 10.0.100 or newer. If the lab machine uses an older SDK, check the compatible release instead of assuming the newest ILSpy version will install.

In a disposable environment with a suitable SDK and a trusted assembly, install the optional tool and display IL from the compiled file:

dotnet tool install --global ilspycmd ilspycmd --help ilspycmd --ilcode ./artifacts/CilWorkshop.dll

The output may contain type definitions, an entry-point method, ldstr instructions for strings, calls into System.Console, and metadata tokens. The exact tokens and method layout vary with compiler and target framework. Those features should be observed in the real output rather than copied from a diagram and reported as a runtime test.

OurTechRoom, “How to Use Ildasm (IL Disassembler) to Inspect .NET DLL and EXE Files?” (November 21, 2025). A demonstration of viewing IL, types, methods and metadata from managed files.

Safety, Compatibility, and Troubleshooting

  • dotnet command is missing: Confirm the installed SDK and its command search path. Review the Windows and Linux PATH explainer.
  • No DLL in the output folder: Verify that dotnet build completed successfully and that the terminal is in the correct project directory.
  • Unexpected name or version: Inspect the project properties and compare the actual DLL with the file the reflection program is executing.
  • ILSpy installation fails: Check the current SDK requirements and PATH. Use a compatible installer only from a trustworthy source.
  • The file is not a managed assembly: Do not confuse an application host or a native library with the project’s managed DLL.
  • Unknown binary inspection: Never load untrusted assemblies into a privileged process. Microsoft warns that malformed PE and metadata can cause failures in reader tools.

Exercises

  1. Name the four typical elements of a managed assembly and explain which one carries the program’s identity.
  2. Explain the difference between CIL and the native instructions the processor ultimately executes.
  3. Build the CilWorkshop sample and identify the managed DLL without relying on its filename alone.
  4. Use the reflection example to record the assembly name and actual version from your own environment.
  5. Describe what the metadata token 0x06000004 represents in Microsoft’s teaching example.
  6. Explain why a native application host can be present beside a managed .NET assembly.

Knowledge Check — Answer Key

  1. The manifest identifies the assembly; type metadata describes types and members, CIL contains managed method instructions, and optional resources contain embedded data.
  2. CIL is intermediate code that generally requires runtime translation; native instructions are CPU-specific and can be executed by that processor.
  3. The sample normally produces CilWorkshop.dll in the selected build output, but a valid managed file must contain the appropriate managed metadata.
  4. The version is whatever the build reports through Assembly.GetName(), not a universal value invented for all SDKs.
  5. The high byte 0x06 denotes a MethodDef table entry and the low bytes specify row four. The value is illustrative, not measured from the sample.
  6. An application host can be native machine code responsible for launching the managed runtime, while a managed DLL holds CIL and metadata.

Sources and Further Reading

Elementary Conclusion

A .NET assembly is a structured binary containing enough information for a compatible runtime to load and understand the program. CIL describes intermediate operations; metadata identifies types, methods and references; the manifest gives the assembly a name and version. The runtime then applies an execution strategy, commonly JIT compilation, to produce the machine instructions that do the work.

Understanding those boundaries helps developers distinguish source-code errors, missing SDK tools, invalid dependencies and runtime-loading failures. The next .NET curriculum lesson will explore the SDK and project structure in greater detail.

Editor’s Note: OSDotNet.003 follows the published CLR lesson. CIL examples and expected output are documented technical illustrations, not independently executed commands; video playback and mobile-browser layout require separate final checks. Microsoft and Ecma standards are cited for the underlying binary and instruction models.

Original BitcoinVersus.Tech donation QR code for 5,000 satoshis.

For mobile scanning, open this lesson on a larger screen and use a compatible Bitcoin wallet to scan the original QR code. Verify the destination before donating 5,000 sats: 3C9o19EH5HSiwEPyCTmEKzxhNCbo2X6TTb.

We volunteer daily to ensure the credibility of the information on this platform is Verifiably True. If you would like to support to help further secure the integrity of our research initiatives, please donate using the QR code provided.

BitcoinVersus.tech is not a financial advisor. This media platform reports on financial subjects purely for informational purposes.

Leave a Reply