2

As we know you can point to a constructor as a Func<T> like this:

Func<MyObject> constructor = () => new MyObject();
var newObject = constructor();

But is there a way to make a constructor for an object you know inherits from MyObject, but you don't know its exact type?

Type inheritedObjectType = obj; // Parameter
Func<MyObject> constructor = () => new MyObject(); // as inheritedObjectType
var newInheritedObject = constructor; // Should now be of the inherited type

An answer with using Activator or anything returning Object is not an option.

Edit: I don't know what type the derived type is at compile time. I only have a System.Type.

Seb Nilsson
  • 26,200
  • 30
  • 103
  • 130

2 Answers2

6

You can use expression trees to build and compile a delegate that will create your derived type:

Func<TBase> CreateDelegate<TBase>(Type derived)
{
    var ctor = derived.GetConstructor(Type.EmptyTypes);

    if (ctor == null)
    {
        throw new ArgumentException("D'oh! No default ctor.");
    }

    var newExpression = Expression.Lambda<Func<TBase>>(
        Expression.New(ctor, new Expression[0]), 
        new ParameterExpression[0]);

    return newExpression.Compile();
}

You can simply call it as follows:

Func<MyBase> create = CreateDelegate<MyBase>(typeof(Derived));

MyBase instance = create();

When you cache that delegate, you will get maximum performance.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • 1
    btw: this is exactly what Dependency Injection frameworks will do for you. Most framework will allow a syntax as follows to register: `container.Register(typeof(MyBase), derived)` and you can get a new instance as follows: `container.GetInstance()` et voila. – Steven May 30 '11 at 14:37
2

Does this program exhibit the desired behavior?

class MyObject{}
class Derived : MyObject {}
internal class Program
{

    public static void Main()
    {
        // the type you want to construct
        Type type = typeof (Derived);

        MethodInfo getConstructor = MakeConstructorGetter(type);
        Func<MyObject> constructor = (Func<MyObject>)getConstructor.Invoke(null, null);

        var obj = constructor();
        Console.WriteLine(obj.GetType());
    }

    private static MethodInfo MakeConstructorGetter(Type type)
    {
        MethodInfo mi = typeof(Program).GetMethod("GetObjectConstructor", BindingFlags.Static | BindingFlags.NonPublic);
        var getConstructor = mi.MakeGenericMethod(type);
        return getConstructor;
    }

    private static Func<T> GetObjectConstructor<T>() where T : MyObject, new()
    {
        return () => new T();
    }
}

MakeConstructorGetter will throw an exception if the type described by Type does not match the generic constraints. So, this will fail in execution time rather than during compilation, but I guess that is a tradeoff you need to make when dealing with generics dynamically like this.

Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • I don't know what "Derived" is other than a System.Type. – Seb Nilsson May 30 '11 at 14:08
  • @Seb: Updated the answer so that it constructs a generic method for the given `Type`. – Fredrik Mörk May 30 '11 at 14:14
  • Hmm, the "getConstructor.Invoke" is basically like Activator.CreateInstance and returns an Object. I want to reference the constructor-lambda for performance, which the reflection-based equivalent doesn't quite have. If it can't be done, then that's the correct answer and you're solution is the closest one can get. – Seb Nilsson May 30 '11 at 14:18
  • 1
    @Seb: note that `getConstructor.Invoke` returns the generic constructor but *does not construct the object*; the object is created by the constructed generic call (so there should be no reflection there). You should easily be able to cache the generated constructors per type. – Fredrik Mörk May 30 '11 at 14:22
  • Does that actually work? I don't think `getConstructor.Invoke(null, null)` returns a `Func` but simply something that inherits from `MyObject`. – Steven May 30 '11 at 14:39
  • @Steven: `getConstructor.Invoke` does indeed return a `Func`. There is a lot of syntactic sugar in there, but essentially `return () => new T();` is equivalent of `return new Func(delegate { return new T(); });` – Fredrik Mörk May 30 '11 at 16:02