|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThere are a number ways to dynamically instantiate objects at runtime. There are some methods built-in to the .NET Framework, such as the This article assumes an understanding of Reflection and a bit about MSIL. I'm not an MSIL guru in any form, so I won't spend much time on the subject. I had to experiment quite a bit, but luckily, there are some great tools to make experimenting easier. See the References section for details. BackgroundThe contents of this article are not new. These performance benchmarks have been performed many times. The References section lists articles that I've read and used to compile this article. My intent is to provide a comparison based on my understanding, as well as to demonstrate how a factory can be created to make the creation of dynamic objects as fast as possible. I look forward to comments and suggestions. Common Methods of Creating Dynamic ObjectsThe fastest way to instantiate an object is by directly calling its constructor. Dim MyWidget As New SuperWidget
This, however, does not allow us to write generic, type-independent code. There are often times that a
I'll use the following Dim WidgetType As Type = Type.GetType("SuperWidget")
And, all our Public MustInherit Class Widget
Public MustOverride Sub Initialize(ByVal host As Object)
End Class
An Interface could also be used. I prefer a The System.Activator Class:Without a doubt, the easiest, most common way to dynamically instantiate objects at runtime. Dim MyWidget As Widget = DirectCast(Activator.CreateInstance(WidgetType), Widget)
MyWidget.Initialize(...)
Though, 'easy' comes at a price; it's by far the worst performing. In certain cases, performance may not be a big concern. If you only need to create a few objects here and there, then it's most likely OK. However, if you have a need to frequently create many dynamic objects, the System.Reflection - GetConstructor:Another simple method is to use Reflection. The following shows how to instantiate an object by obtaining its Dim CtorInfo As ConstructorInfo = WidgetType.GetConstructor(System.Type.EmptyTypes)
Dim MyWidget As Widget = DirectCast(CtorInfo.Invoke(New Object() {}), Widget)
Invoking the constructor, if cached, out-performs the System.Reflection - GetMethod + Delegate:Yet another method is to create and cache a Public Delegate Function GetInstanceDelegate() As Widget
Dim GetInstanceMethod As MethodInfo = _
WidgetType.GetMethod( _
"GetWidgetInstance", _
BindingFlags.Public Or BindingFlags.Static)
Dim GetInstanceDelegate As GetInstanceDelegate = _
DirectCast( _
System.Delegate.CreateDelegate( _
GetType(GetInstanceDelegate), GetInstanceMethod), _
GetInstanceDelegate)
The problem here is that you must have a Public Class SuperWidget
Inherits Widget
' ...
Public Shared Function GetWidgetInstance() As Widget
Return New SuperWidget
End Function
End Class
Because it's System.Runtime.Serialization - FormatterServices.GetUninitializedObject:(Addendum) I stumbled across the In the previous example, a Add the Public MustInherit Class Widget
Public MustOverride Sub Initialize(ByVal host As Object)
Public MustOverride Function GetInstance() As Widget
End Class
Override it as required, and return an instance. If other initialization parameters are required, they can be added to the Public Class WonderWidget
Inherits Widget
' ...
Public Overrides Function GetInstance() As Widget
Return New WonderWidget
End Function
End Class
Each Dim WidgetFactory As Widget = DirectCast( _
FormatterServices.GetUninitializedObject(WidgetType), Widget)
Dim MyWidget As Widget = WidgetFactory.GetInstance()
System.Reflection.Emit - The Runtime Dynamic Factory:To achieve the same performance as the Public Interface IWidgetInstanceMachine
Function GetInstance() As Widget
End Interface
The following code shows how each machine is created by building a runtime assembly: Private Shared Function GetWidgetMachine(ByVal widgetType As Type) As IWidgetInstanceMachine
Dim Name As String = widgetType.FullName & "InstanceMachine"
Dim AssemblyName As AssemblyName = New AssemblyName
AssemblyName.Name = Name
Dim SavePath As String = "C:\Temp" ' Uncomment below to save.
Dim TypeName As String = widgetType.Name & "Machine"
' Define the assembly...
Dim AssemblyBuilder As AssemblyBuilder = _
Thread.GetDomain().DefineDynamicAssembly( _
AssemblyName, _
AssemblyBuilderAccess.RunAndSave, _
SavePath)
Dim ModuleBuilder As ModuleBuilder = _
AssemblyBuilder.DefineDynamicModule("Machine", Name & ".dll")
' Define the class "Public Class xxxWidgetMachine"
Dim TypeBuilder As TypeBuilder = _
ModuleBuilder.DefineType(TypeName, TypeAttributes.Public)
' Implement the interface "Implements IWidgetInstanceMachine"
TypeBuilder.AddInterfaceImplementation(GetType(IWidgetInstanceMachine))
' Define the constructor "Public Sub New()"
TypeBuilder.DefineDefaultConstructor(MethodAttributes.Public)
' Define the input parameter array. In this case there are none but,
' If you want to pass parameters, simply list the Types in
' the braces: {GetType(String), GetType(Integer)}
Dim InParamTypes As Type() = New Type() {}
' Define the return Type... Note: This MUST match the return Type
' defined in the Intereface, otherwise you'll get a runtime error
' indicating that the interface has not been implemented.
Dim ReturnType As Type = GetType(Widget)
' Define the "GetInstance()" method...
Dim GetInstance_MethodBuilder As MethodBuilder = _
TypeBuilder.DefineMethod("GetInstance", _
MethodAttributes.Public Or MethodAttributes.Virtual, _
ReturnType, InParamTypes)
' Get the Widget's constructor. In this case, we have no input
' parameters so the derived class', System.Object, contructor
' will be called.
Dim WidgetCtor As ConstructorInfo = widgetType.GetConstructors()(0)
' Get an IL generator to emit the body of the GetInstance method...
Dim GetInstance_ILGenerator As ILGenerator = _
GetInstance_MethodBuilder.GetILGenerator
' Two OpCode's emit the body of the GetInstance method:
' "Return New xxxWidget"
' ----------------------------------------
' 1. Creates the Widget and pop's it onto the stack...
GetInstance_ILGenerator.Emit(OpCodes.Newobj, WidgetCtor)
' 2. Return's the Widget from the stack...
GetInstance_ILGenerator.Emit(OpCodes.Ret)
' ----------------------------------------
TypeBuilder.CreateType()
' Uncomment to save the assembly...
'AssemblyBuilder.Save(Name & ".dll")
Return DirectCast(AssemblyBuilder.CreateInstance(TypeName), IWidgetInstanceMachine)
End Function
Because of my limited knowledge of MSIL, I used Reflector in addition to a bit of trial and error to get the desired results. If we save and open one of the machine assemblies, we'll see the following...
Another helpful tool is DILE. You can see the two OpCodes that were emitted by the ILGenerator. Creating/hard-coding a single machine, then compiling and reviewing the IL shows the OpCodes you need to emit to reproduce the machine at runtime.
And finally, when all the machines are running, the Public Class WidgetFactory
Public Shared Function GetWidget(ByVal widgetType As Type) As Widget
Return DirectCast(_MachineCache(widgetType), _
IWidgetInstanceMachine).GetInstance()
End Function
' ...
End Class
Points of InterestThe References & Credits
History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||