What's the best way (in .NET 4) to create an instance of a type determined at runtime.
I have an instance method which although acting on a BaseClass object may be called by instances of its derived classes. I require to create another instance of the same type as this
within the method. Overloading the Method for each derived class is not practical as it is fairly involved and would be more efficient to keep to the single implementation.
public class BaseClass
{
//constructors + properties + methods etc
public SomeMethod()
{
//some code
DerivedClass d = new DerivedClass(); //ideally determine the DerivedClass type at run-time
}
}
I've read a bit about reflection or using the dynamic keyword but i don't have experience with these.
I know this was marked as reflection, but I generally see reflection as a last resort for performance and complexity reasons. There are some instances where your design/usage requires reflection; however, I'll offer some alternatives for consideration:
Use a factory Func
:
public void SomeMethod(Func<BaseClass> createDerived)
{
BaseClass d = createDerived();
}
Make your method use a constrained generic type:
public void SomeMethod<TDerived>() where TDerived : BaseClass, new()
{
TDerived d = new TDerived();
}
Under the covers this last alternative makes use of Activator.CreateInstance
as others have suggested. I prefer the last to reflection because they both require a parameter-less constructor, but the compiler enforces the constraint that the derived type must have a parameterless constructor whereas the reflection approach results in a runtime exception.
Best way for performance to repeatedly create instance in runtime is compiled expression:
static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
X x = YCreator();
Statistics (2012):
Iterations: 5000000
00:00:00.8481762, Activator.CreateInstance(string, string)
00:00:00.8416930, Activator.CreateInstance(type)
00:00:06.6236752, ConstructorInfo.Invoke
00:00:00.1776255, Compiled expression
00:00:00.0462197, new
Statistics (2015, .net 4.5, x64):
Iterations: 5000000
00:00:00.2659981, Activator.CreateInstance(string, string)
00:00:00.2603770, Activator.CreateInstance(type)
00:00:00.7478936, ConstructorInfo.Invoke
00:00:00.0700757, Compiled expression
00:00:00.0286710, new
Statistics (2015, .net 4.5, x86):
Iterations: 5000000
00:00:00.3541501, Activator.CreateInstance(string, string)
00:00:00.3686861, Activator.CreateInstance(type)
00:00:00.9492354, ConstructorInfo.Invoke
00:00:00.0719072, Compiled expression
00:00:00.0229387, new
Full code:
public static X CreateY_New()
{
return new Y();
}
public static X CreateY_CreateInstance()
{
return (X)Activator.CreateInstance(typeof(Y));
}
public static X CreateY_CreateInstance_String()
{
return (X)Activator.CreateInstance("Program", "Y").Unwrap();
}
static readonly System.Reflection.ConstructorInfo YConstructor =
typeof(Y).GetConstructor(Type.EmptyTypes);
static readonly object[] Empty = new object[] { };
public static X CreateY_Invoke()
{
return (X)YConstructor.Invoke(Empty);
}
static readonly Func<X> YCreator = Expression.Lambda<Func<X>>(
Expression.New(typeof(Y).GetConstructor(Type.EmptyTypes))
).Compile();
public static X CreateY_CompiledExpression()
{
return YCreator();
}
static void Main(string[] args)
{
const int iterations = 5000000;
Console.WriteLine("Iterations: {0}", iterations);
foreach (var creatorInfo in new []
{
new {Name = "Activator.CreateInstance(string, string)", Creator = (Func<X>)CreateY_CreateInstance},
new {Name = "Activator.CreateInstance(type)", Creator = (Func<X>)CreateY_CreateInstance},
new {Name = "ConstructorInfo.Invoke", Creator = (Func<X>)CreateY_Invoke},
new {Name = "Compiled expression", Creator = (Func<X>)CreateY_CompiledExpression},
new {Name = "new", Creator = (Func<X>)CreateY_New},
})
{
var creator = creatorInfo.Creator;
var sum = 0;
for (var i = 0; i < 1000; i++)
sum += creator().Z;
var stopwatch = new Stopwatch();
stopwatch.Start();
for (var i = 0; i < iterations; ++i)
{
var x = creator();
sum += x.Z;
}
stopwatch.Stop();
Console.WriteLine("{0}, {1}", stopwatch.Elapsed, creatorInfo.Name);
}
}
public class X
{
public X() { }
public X(int z) { this.Z = z; }
public int Z;
}
public class Y : X { }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With