Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to create an instance of run-time determined type [duplicate]

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.

like image 389
gwizardry Avatar asked Mar 20 '12 14:03

gwizardry


2 Answers

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.

like image 104
devgeezer Avatar answered Oct 14 '22 21:10

devgeezer


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 { }
like image 32
Serj-Tm Avatar answered Oct 14 '22 22:10

Serj-Tm