Export (0) Print
Expand All

Defining a Dynamic Assembly

Types are defined in the scope of a dynamic module by using the ModuleBuilder.DefineType and ModuleBuilder.DefineEnum methods. DefineType returns a TypeBuilder. Type names are always specified as full names that include the namespace. DefineEnum returns an EnumBuilder; fields are added to an enumeration by using the EnumBuilder.DefineLiteral method, as demonstrated by the code example for that method.

When you define a type, you can specify its name, attributes, base class, and implemented interfaces. The base class and implemented interfaces can be set later by using the SetParent and AddInterfaceImplementation methods, respectively. In Silverlight-based applications, packing size and class ize cannot be set.

Before you use a type, you must call the TypeBuilder.CreateType method. CreateType completes the creation of the type. Following the call to CreateType, the caller can instantiate the type (by using the Activator.CreateInstance method or by invoking the type's constructors) and invoke members of the type (by using the Type.InvokeMember method). It is an error to invoke methods that change the implementation of a type after CreateType has been called. For example, the common language runtime throws an exception if the caller tries to add new members to a type.

A class initializer is created by using the TypeBuilder.DefineTypeInitializer method, which returns a ConstructorBuilder object.

Nested types are defined by using one of the TypeBuilder.DefineNestedType methods.

Attributes and Custom Attributes

Use the TypeAttributes enumeration to specify the attributes of types, including visibility. Type attributes are specified only when the type is defined. Some notes about type attributes:

  • Interfaces are specified by using the Interface and Abstract attributes.

  • Classes that cannot be inherited (NotInheritable in Visual Basic, sealed in C#) are specified by using the TypeAttributes.Sealed attribute.

  • If TypeAttributes.SequentialLayout is specified, the class loader lays out fields in the order they are read from metadata. The class loader considers the specified packing size but ignores any specified field offsets. The metadata preserves the order in which the field definitions are emitted. Even across a merge, the metadata will not reorder the field definitions. The loader will honor the specified field offsets only if TypeAttributes.ExplicitLayout is specified.

Custom attributes are applied by using the SetCustomAttribute(ConstructorInfo, Byte[]) method overload.

Known Issues

  • Reflection emit does not verify whether a non-abstract class that implements an interface has implemented all the methods declared in the interface. However, if the class does not implement all the methods declared in an interface, the runtime does not load the class.

  • Although TypeBuilder is derived from Type, some of the abstract methods defined in the Type class are not fully implemented in TypeBuilder. These TypeBuilder methods throw the NotSupportedException. The desired functionality can be obtained by retrieving the created type by using Type.GetType or Assembly.GetType, and reflecting on the retrieved type.

Use the ILGenerator class to emit Microsoft intermediate language (MSIL) for the bodies of methods and constructors. The ConstructorBuilder.GetILGenerator method returns an ILGenerator for a constructor. The MethodBuilder.GetILGenerator method returns an ILGenerator for a method.

Note Note:

There is no MSIL generator for properties or events, because the MSIL for these member types is in the accessor methods (get, set, add, remove) and in the other methods that can be associated with properties and events.

When dynamic methods are executed, the just-in-time (JIT) compiler is called to convert the MSIL to native code. Exceptions can be thrown at this point if the JIT compiler detects an invalid program. Usually TargetInvocationException is thrown, in which case you can get the exception that indicates the actual cause of the problem by using the InnerException property. Common inner exceptions include the following:

  • InvalidProgramException is thrown for many errors. Some examples: The method body exceeds an internal limitation, such as the maximum allowed size; there is nothing on the execution stack when the OpCodes.Ret instruction is encountered and the method returns a value; the execution stack is not empty when the method does not return a value; the handlers in an exception block are out of order.

  • VerificationException is thrown if the type safety of the method body cannot be verified.

Isolating the problem can be difficult, because dynamic assemblies cannot be saved to disk in Silverlight. You might find the following techniques useful:

  • Write a C# or Visual Basic program with a method that does what you want your method to do, compile it, and examine the MSIL in the compiled assembly with Ildasm.exe (which is a disassembly tool included in Visual Studio).

  • Try compiling the program that emits the problematic MSIL in the full .NET Framework instead of as a Silverlight-based application. In the full .NET Framework, you can save dynamic assemblies to disk and use the Peverify.exe tool to identify code generation problems.

Program Logic

Use the overloads of the Emit method to emit MSIL instructions. The OpCodes enumeration lists the available instructions. Emit takes an instruction, or an instruction and one or more targets. Instructions can operate on the target, and/or on items that have been pushed onto the execution stack. Some examples:

To control program logic, comparison and branching instructions are provided. MSIL is not structured code; branching instructions require a label or a code location as a target. Define a label at the beginning of code generation or at the point where you first want to use it as the target of a branching operation, by using the DefineLabel method. The label can be used immediately as the target of branching instructions. When you reach the location in your emitted code that you want the label to mark, call the MarkLabel method.

Switch tables can be implemented by using the Emit(OpCode, Label[]) method overload and specifying an array of labels

Exceptions and Exception Handling

To throw an exception with its default message, use the ThrowException method. ThrowException uses the default constructor to create the specified exception. In all other cases, create an instance of the desired exception by using the appropriate constructor, and emit a Throw instruction to pop the instance off the execution stack and throw it.

Implement structured exception handling by using exception blocks. The ILGenerator.BeginExceptionBlock method begins an exception block, and the EndExceptionBlock method ends it. ILGenerator.BeginExceptionBlock returns a label that you can use as the target of the OpCodes.Leave instruction, to exit from the protected code block or to exit from handlers; you cannot branch out of exception blocks. Within the exception block, you can include the following handlers:

Note Note:

For a try/catch/finally example, see the ILGenerator.BeginExceptionBlock method.

For catch handlers, the sequence of calls should resemble the following template (some elements can be omitted or repeated):

Emit some MSIL.
BeginExceptionBlock
Emit the MSIL for the try block.
BeginCatchBlock
Emit the MSIL for the handler of the first exception type.
BeginCatchBlock
Emit the MSIL for the handler of the second exception type, or for a more general exception type. You can provide many catch handlers.
BeginFaultBlock
Emit the MSIL for the fault block.
BeginFinallyBlock
Emit the MSIL for the finally block.
EndExceptionBlock

For filtered handlers, the sequence of calls should resemble the following template (some elements can be omitted or repeated):

Emit some MSIL.
BeginExceptionBlock
Emit the MSIL for the try block.
BeginExceptFilterBlock
Emit the MSIL for the filtered exception handler.
BeginCatchBlock
Emit the MSIL for the catch block. The catch handler should be supplied with a null type.
BeginFaultBlock
Emit the MSIL for the fault block.
BeginFinallyBlock
Emit the MSIL for the finally block.
EndExceptionBlock

You define a constructor by using the TypeBuilder.DefineConstructor method, which returns a ConstructorBuilder. DefineConstructor requires the caller to specify the constructor attributes by using the MethodAttributes enumeration.

The default constructor for a class is defined by using the TypeBuilder.DefineDefaultConstructor method, which returns a ConstructorBuilder. The default constructor just calls the constructor of the parent class; you cannot add more code. If you need a parameterless constructor that does more than call the base class constructor, you must define it by using the DefineConstructor method. The common language runtime automatically defines a default constructor for a class if you not define any constructors. See the ConstructorInfo class for more information.

Attributes and Custom Attributes

Use the MethodAttributes enumeration to specify the attributes of constructors, including visibility. Constructor attributes are specified only when the constructor is defined. Some notes about constructor attributes:

To set custom attributes, use the SetCustomAttribute(CustomAttributeBuilder) method overload.

Constructors can also have implementation attributes, which are specified by using the MethodImplAttributes enumeration with the ConstructorBuilder.SetImplementationFlags method. Implementation attributes are not generally useful in Silverlight-based applications.

Known Issues

Reflection emit can define global methods as well as methods that are members of types. Both kinds of methods are represented by MethodBuilder objects.

Note Note:

This topic discusses only methods that are defined in dynamic assemblies, either in types or in modules. You can also use the DynamicMethod class to define anonymously hosted dynamic methods that can be reclaimed by garbage collection when they are no longer needed. See How to: Define and Execute Dynamic Methods.

A global method is defined by using the ModuleBuilder.DefineGlobalMethod method. Global methods must be static.

A method is defined as a type member by using the TypeBuilder.DefineMethod method.

The DefineParameter method is used to set the name and parameter attributes of a parameter, or of the return value. The ParameterBuilder object returned by this method represents a parameter or the return value. The ParameterBuilder object can be used to set the marshaling, to set the constant value, and to apply custom attributes.

Attributes and Custom Attributes

Use the MethodAttributes enumeration to specify a method's visibility, and whether the method is static, sealed, virtual, or abstract (Shared, NotOverridable, Overridable, or MustOverride in Visual Basic). Method attributes are specified only when a method is defined. Some notes about method attributes:

Custom attributes are applied by using the SetCustomAttribute(CustomAttributeBuilder) method overload.

Methods can also have implementation attributes, which are specified by using the MethodImplAttributes enumeration with the MethodBuilder.SetImplementationFlags method. Implementation attributes are not generally useful in Silverlight-based applications.

Known Issues

You define a field as a type member by using the TypeBuilder.DefineField method. The DefineField method requires the caller to specify the field name, the field type, and the field attributes (by using the FieldAttributes enumeration). DefineField returns a FieldBuilder. SetConstant defines the default value of a field.

In Silverlight, you cannot control field layout or reference data in the .sdata section of the portable executable (PE) file.

Attributes and Custom Attributes

Use the FieldAttributes enumeration to specify visibility and whether a field is static. Field attributes are set only when a field is defined. Some notes about field attributes:

Use the SetCustomAttribute(CustomAttributeBuilder) method overload to apply custom attributes.

You define a property by using the TypeBuilder.DefineProperty method, which returns a PropertyBuilder. The DefineProperty method requires the caller to specify the property name, the signature of the property, the property attributes (by using the PropertyAttributes enumeration), and the indexes (if the property is indexed).

Use the PropertyBuilder.SetGetMethod and PropertyBuilder.SetSetMethod methods to specify the get and set accessor methods. Use the TypeBuilder.DefineMethod method to define the accessor methods; for consistency with .NET Framework properties, use the prefixes "get_" and "set_" for the accessor names. For a code example, see the TypeBuilder.DefineProperty(String, PropertyAttributes, Type, Type[]) method overload.

The default value of the property can be set by using the PropertyBuilder.SetConstant method.

Attributes and Custom Attributes

The attributes of properties are very limited. Use the PropertyAttributes enumeration to specify whether a property has a default value or a name that has special significance. The important characteristics of properties, such as visibility, are controlled by the attributes of their get and set accessor methods. Use the MethodAttributes enumeration to specify these attributes, as described in the notes about method attributes. Some notes about the attributes of accessor methods:

  • The get and set accessor methods can have different visibility.

  • The MethodAttributes.Final, Virtual, and Abstract attributes of get and set accessor methods are not required to match. However, differences in these attributes can create usability problems.

  • The get and set accessor methods must have the MethodAttributes.SpecialName attribute.

Use the SetCustomAttribute(CustomAttributeBuilder) method overload to apply custom attributes to properties, and the SetCustomAttribute(CustomAttributeBuilder) method overload to apply custom attributes to accessor methods.

You define an event by using the TypeBuilder.DefineEvent method, which returns an EventBuilder. The DefineEvent method requires the caller to specify the event name, the event attributes (by using the EventAttributes enumeration), and the delegate type that describes the event signature.

Use the EventBuilder.SetAddOnMethod and EventBuilder.SetRemoveOnMethod methods to specify the add and remove accessor methods for the event. Use the TypeBuilder.DefineMethod method to define the accessor methods; for consistency with .NET Framework properties, use the prefixes "add_" and "remove_" for the accessor names.

Attributes and Custom Attributes

Use the EventAttributes enumeration to specify whether the name of an event has special significance. As with properties, the important characteristics of events are controlled by the attributes of their add and remove accessor methods. See the notes about property attributes.

Use the SetCustomAttribute(CustomAttributeBuilder) method overload to apply custom attributes to events, and the SetCustomAttribute(CustomAttributeBuilder) method overload to apply custom attributes to accessor methods.

You define a string constant by using the ModuleBuilder.GetStringConstant method. String constants are scoped to the module in which they are defined.

In Silverlight-based applications, defining resources is not supported for dynamic assemblies.

Reflection emit enables you to define limited symbolic information for a dynamic module. You must specify that a dynamic module will contain symbolic information by using the AssemblyBuilder.DefineDynamicModule(String, Boolean) method overload and specifying true for emitSymbolInfo.

In Silverlight, reflection emit provides the following methods for emitting symbolic information:

Community Additions

ADD
Show:
© 2014 Microsoft