Posted by: Roy Antony Arnold | October 25, 2008

Reflection

Reflection

  • Many of the services available in .NET and exposed via C# (such as late binding, serialization, remoting, attributes, etc.) depend on the presence of metadatas.
  • Manipulating existing types via their metadata termed reflection and is done using a rich set of types in the System.Reflection namespace.
  • Creating new types is termed Reflection.Emit, and is done via the types in the System.Reflectio.Emit namespace.
  • The classes in the Reflection namespace, along with the System.Type and System.TypedReference classes, provide support for examining and interacting with the metadata.

 

Type Hierarchy

  • Reflection involves traversing and manipulating an object model that represents an application, including all its compiler-time and runtime elements.
  • Consequently, it is important to understand the various logical units of a .NET application and their roles and relationships.
  • The fundamental units of an application are its types, which contain members and nested types.
  • In addition to types, an application contains one or more modules and one or more assemblies.
  • All these elements are static and are described in metadata produced by the compiler at compile time.
  • The one exception to this rule is elements (such as types, modules, assemblies, etc.) that are created on the fly via Reflection.Emit.
  • At runtime, these elements are all contained within an AppDomain. This forms the root of the types hierarchy of a .NET application at runtime.
  • In any given application, the relationship between these units is hierarchical, as depicted by the diagram below:

    AppDomain(runtime root of hierarchy)

        Assemblies

            Modules

                Types

                    Members

                    Nested types

AppDomains

  • From the perspective of reflection, an AppDomain is the root of the type hierarchy and serves as the container for assemblies and types when they are loaded into memory at runtime.
  • A helpful way to think about an AppDomain is to view it as the logical equivalent of a process in a Win32 application.
  • AppDomain provide isolation, creating a hard boundary for managed code just like the process boundary under Win32.
  • Similar to processes, AppDomains can be started and stopped independently, and application faults take down only the AppDomain the fault occurs in, not the process hosting the AppDomain.

Assemblies and Modules

  • Assemblies are the logical equivalent of DLLs in Win32 and the basic unit of deployment, versioning, and reuse for types.
  • In addition, assemblies create a security, visibility, and scope resolution boundary for types.
  • A module is a physical file such as a DLL, an EXE, or a resource.
  • An assembly can be composed of multiple modules, allowing controlling application working set size, using multiple languages within one assembly, and sharing a module across multiple assemblies.

Types, members, and nested types

  • The most basic element that reflection deals with is the type.
  • This class represents the metadata for each type declaration in an application.
  • Types contain members, which include constructors, fields, properties, events, and methods.
  • In addition, types may contain nested types, which exist within the scope of an outer type and are typically used as helper classes.
  • Types are grouped into modules, which are, in turn, contained within assemblies.

 

Tasks using Reflection

Reflection is generally used for any of four tasks:

  • Viewing metadata
    • This might be used by tools and utilities that wish to display metadata.
  • Performing type discovery
    • This allows to examine the types in an assembly and interact with or instantiate those types.
    • This can be useful in creating custom scripts.
    • For example, you might want to allow your users to interact with your program using a script language, such as JavaScript, or a scripting language you create yourself.
  • Late binding to methods and properties
    • This allows the programmer to invoke properties and methods on objects dynamically instantiated based on type discovery. This is also known as dynamic invocation.
  • Creating types at runtime (Reflection Emit)
    • The ultimate use of reflection is to create new types at runtime and then to use those types to perform tasks.
    • You might do this when a custom class, created at runtime, will run significantly faster than more generic code created at compile time.

Categories