Introduction
There are a number ways to dynamically instantiate objects at runtime. There are some methods built-in to the .NET Framework, such as the System.Activator
class, Reflection, and ways you can brew your own. Each with its own consequences and payoff's. During my search for the fastest way to dynamically instantiate objects, I consolidated information about the various methods, their practicality, and respective performance. Please note that this article applies to .NET 1.1 - my knowledge of 2.0 is still limited as my primary work environment is (more like "is struggling to be") a .NET 1.1 shop. In the more current versions of .NET, there are additional ways to achieve what I present in this article.
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.
Background
The 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 Objects
The 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 Type
is not know until runtime, so let's examine the following methods of dynamic object creation and their respective performance. The screenshot above shows my benchmark results. If you're already familiar with the following, you may want to skip ahead to the Dynamic Factory section.
System.Activator
- Activator.CreateInstance(type As System.Type)
- Reflection -
ConstructorInfo.Invoke()
- Reflection -
MethodInfo
and a Delegate
System.Runtime.Serialization
- FormatterServices.GetUninitializedObject(type As System.Type)
System.Reflection.Emit
- Emit dynamic assemblies that create object instances!
I'll use the following Type
in the code examples below:
Dim WidgetType As Type = Type.GetType("SuperWidget")
And, all our Widget
s derive from:
Public MustInherit Class Widget
Public MustOverride Sub Initialize(ByVal host As Object)
End Class
An Interface could also be used. I prefer a MustInherit
(abstract) class as I can enforce the scope of its members. But, it's all a mater of what works for the situation.
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.Activator
is not a good choice.
System.Reflection - GetConstructor:
Another simple method is to use Reflection. The following shows how to instantiate an object by obtaining its ConstructorInfo
:
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.Activator
. So, to avoid calling GetConstructor
each time we need a new instance, a Hashtable
is used for caching the Type
and, in this case, the ContructorInfo
. While insignificant, the Hashtable
lookups will incur a small performance hit.
System.Reflection - GetMethod + Delegate:
Yet another method is to create and cache a Delegate
. Without a Hashtable
/Cache lookup, this method is just about as fast as direct instantiation, but has some serious problems. The following shows how it's done:
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 Shared
(static
in C#) method in each Widget
to return the correct instance type. And, it must be the same in all your Widget
s.
Public Class SuperWidget
Inherits Widget
Public Shared Function GetWidgetInstance() As Widget
Return New SuperWidget
End Function
End Class
Because it's Shared
, you cannot enforce its existence with an Interface
or base class. You just have to assume it's there and spelled correctly. Not a practical approach if you are creating an API or application for distribution. But, there is a solution...
System.Runtime.Serialization - FormatterServices.GetUninitializedObject:
(Addendum) I stumbled across the FormatterServices
class while digging through Microsoft's ObjectBuilder DI Framework. I was curious as to what methods were being used to instantiate objects. Upon investigation, I found that when an object is de-serialized, an uninitialized instance of it is created by the deserializer. In other words, it's instantiated without calling any of its constructors. Neat! Subsequently, the object's members are repopulated with the previously serialized values.
In the previous example, a Shared
method within each Widget
returned an instance of the respective type. Now, we can essentially achieve the same or better performance without an unenforceable Shared
method. This allows the GetWidgetInstance()
method to be enforced by an Interface
or MustOverride
in the base class. Here's how it's done:
Add the MustOverride Function GetInstance()
to the base class.
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 GetInstance()
method in the base class, i.e., Public MustOverride Function GetInstance(host as Object) As Widget
.
Public Class WonderWidget
Inherits Widget
Public Overrides Function GetInstance() As Widget
Return New WonderWidget
End Function
End Class
Each Widget
becomes its own factory. Not entirely dynamic, as you still need to hard code the type being returned. Though, the nice thing here is that you can easily run additional code, call methods, and set properties before returning the Widget
. The following WidgetFactory
, a Widget
itself, is just an instance of a specific Widget
type. However, no constructor calls were made, so any initialization code was never executed. Because of this, it's possible that calls to other members will result in an exception. But that's okay for our purposes. A generic factory class can conceal the details to prevent this from happening as well as cache each uninitialized Widget
. To pump out instances, you simply...
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 Delegate
method and still enforce our members by way of a base class or Interface
, we can use the Reflection.Emit
namespace to generate runtime "machine" assemblies for each Widget
. Each machine returns only a single type of Widget
. A WidgetFactory
is responsible for creating the machines and returning Widget
s from them. Each machine is cached in a Hashtable
, keyed by the Type
of Widget
it creates. To achieve maximum performance, the assembly (or AppDomain) is pre-scanned for Widget
s at startup. Because the machines are pre-built, an extra Hashtable
lookup to check for their existence on every call is avoided. To build a WidgetInstanceMachine
, we first need to define an Interface
.
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"
Dim TypeName As String = widgetType.Name & "Machine"
Dim AssemblyBuilder As AssemblyBuilder = _
Thread.GetDomain().DefineDynamicAssembly( _
AssemblyName, _
AssemblyBuilderAccess.RunAndSave, _
SavePath)
Dim ModuleBuilder As ModuleBuilder = _
AssemblyBuilder.DefineDynamicModule("Machine", Name & ".dll")
Dim TypeBuilder As TypeBuilder = _
ModuleBuilder.DefineType(TypeName, TypeAttributes.Public)
TypeBuilder.AddInterfaceImplementation(GetType(IWidgetInstanceMachine))
TypeBuilder.DefineDefaultConstructor(MethodAttributes.Public)
Dim InParamTypes As Type() = New Type() {}
Dim ReturnType As Type = GetType(Widget)
Dim GetInstance_MethodBuilder As MethodBuilder = _
TypeBuilder.DefineMethod("GetInstance", _
MethodAttributes.Public Or MethodAttributes.Virtual, _
ReturnType, InParamTypes)
Dim WidgetCtor As ConstructorInfo = widgetType.GetConstructors()(0)
Dim GetInstance_ILGenerator As ILGenerator = _
GetInstance_MethodBuilder.GetILGenerator
GetInstance_ILGenerator.Emit(OpCodes.Newobj, WidgetCtor)
GetInstance_ILGenerator.Emit(OpCodes.Ret)
TypeBuilder.CreateType()
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 WidgetFactory
can retrieve a machine and get a Widget
from it.
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 Interest
The IWidgetInstanceMachine
is a very simple implementation. With a little extra effort, you can pass parameters through the machine's GetInstance(param1, ...)
method to be used when a Widget
is created. This allows you to create more complex machines that behave more like factories themselves.
References & Credits
- An excellent article with a similar intent - Fast Dynamic Property Access with C# - Gave me the start I needed.
- A somewhat dated, but ever still excellent discussion on the topic can be found at Performance of Dynamic Object Instantiation. I also used pieces of the performance benchmarking code, and was enlightened by the Delegate approach.
- Lutz Roeder's Reflector is the best thing since sliced bread.
- DILE, an MSIL editor and debugger, is also a nice way to get all the way under the hood of your code.
History
- 10/23/2007 - Initial creation.
- 11/27/2007 - Added the
FormatterServices.GetUninitializedObject
method and updated the demo project.