a constructor as a delegate - is it possible in C#?

C#DelegatesConstructor

C# Problem Overview


I have a class like below:

class Foo
{
  public Foo(int x) { ... }
}

and I need to pass to a certain method a delegate like this:

delegate Foo FooGenerator(int x);

Is it possible to pass the constructor directly as a FooGenerator value, without having to type:

delegate(int x) { return new Foo(x); }

?

EDIT: For my personal use, the question refers to .NET 2.0, but hints/responses for 3.0+ are welcome as well.

C# Solutions


Solution 1 - C#

I'm assuming you would normally do something like this as part of a factory implementation, where the actual types aren't known at compile-time...

First, note that an easier approach may be a post-create init step, then you can use generics:

static T Create<T>({args}) where T : class, ISomeInitInterface, new() {
    T t = new T();
    t.Init(args);
    return t;
}

You can then use MakeGenericMethod and/or CreateDelegate.


Otherwise; you can do this with on the fly with Expression (3.5) or DynamicMethod (2.0).

The Expression approach is easier to code:

    var param = Expression.Parameter(typeof(int), "val");
    var ctor = typeof(Foo).GetConstructor(new[] { typeof(int) });
    var lambda = Expression.Lambda<Func<int, Foo>>(
        Expression.New(ctor, param), param);
    var func = lambda.Compile();
    Foo foo = func(123);
    string s = foo.ToString(); // proof

or (using DynamicMethod):

    ConstructorInfo ctor = typeof(Foo).GetConstructor(new[] { typeof(int) });
    DynamicMethod dm = new DynamicMethod("Create", typeof(Foo),
            new Type[] { typeof(int) }, typeof(Foo), true);
    ILGenerator il = dm.GetILGenerator();
    il.Emit(OpCodes.Ldarg_0);
    il.Emit(OpCodes.Newobj, ctor);
    il.Emit(OpCodes.Ret);
    Converter<int, Foo> func = (Converter<int, Foo>)
        dm.CreateDelegate(typeof(Converter<int, Foo>));        
    Foo foo = func(123);
    string s = foo.ToString(); // proof

Solution 2 - C#

Nope, the CLR does not allow binding delegates to ConstructorInfo.

You can however just create your own:

static T Make<T>(Action<T> init) where T : new()
{
  var t = new T();
  init(t);
  return t;
}

Usage

var t = Make<Foo>( x => { x.Bar = "bar"; x.Baz = 1; });

Solution 3 - C#

I think as concise as you're going to get (without moving to a factory pattern) would be something with anonymous methods, like this:

delegate Foo FooGenerator(int x);

...    

void DoStuff()
{
    YourDelegateConsumer(x => new Foo(x));
}

This isn't doing strictly what you asked for (since you're passing a delegate to an anonymous method that returns a new instance, rather than a direct delegate to the constructor), but I don't think what you're asking for is strictly possible.

This is, of course, assuming you're using 3.5+

Solution 4 - C#

It sounds like you probably want to be using the class factory pattern.

Factory Method Pattern

Solution 5 - C#

Unfortunately not, constructors are not quite the same things as methods and as such you cannot create a delegate that points to them. This is an interesting idea though, perhaps with more information we could devise some sort of workaround that would be syntactically similar.

Solution 6 - C#

Marc Gravell's answer inspired me to the following very simple solution:

static void Main()
{
    Pet a = _MakeObject(typeof(Dog));
    Pet b = _MakeObject(typeof(Cat));
}

private static Pet _MakeObject(Type type)
{
    ConstructorInfo info = type.GetConstructor(new Type[0]);
    return (Pet)info?.Invoke(null);
}

Almost the same thing if your constructor has params (in this example: 1 param of type int):

static void Main()
{
    Pet a = _MakeObject(typeof(Dog), 5);
    Pet b = _MakeObject(typeof(Cat), 7);
}

private static Pet _MakeObject(Type type, int age)
{
    ConstructorInfo info = type.GetConstructor(new [] { typeof(int) });
    return (Pet)info?.Invoke(new object[] { age });
}

Solution 7 - C#

Another option would be to use the Activator class, like so:

Using Generic Types

public delegate Foo FooGeneratorDelegate<T>(int x);

public T FooGeneratorFunction<T>(int x)
{
    return (T)Activator.CreateInstance(typeof(T), x);
}

// implementation example
FooGeneratorDelegate<Foo> del = FooGeneratorFunction<Foo>;
Foo foo = del(someIntValue);

Passing Your Type Foo as a Parameter

public delegate object FooGeneratorDelegate(Type t, int x);

public object FooGeneratorFunction(Type t, int x)
{
    return Activator.CreateInstance(t, x);
}

// implementation example
FooGeneratorDelegate del = FooGeneratorFunction;
Foo foo = (Foo)del(typeof(Foo), someIntValue);

If the Type will Always be of Type Foo

public delegate Foo FooGeneratorDelegate(int x);

public Foo FooGeneratorFunction(int x)
{
    return (Foo)Activator.CreateInstance(typeof(Foo), x);
}

// implementation example
FooGeneratorDelegate del = FooGeneratorFunction;
Foo foo = del(someIntValue);

Solution 8 - C#

While other answers only support parameter-less constructor. Here is the version support parameters. Hope it can help someone who want to turn with-parameters ConsturctorInfo to Delegate.

public Delegate CreateDelegate(ConstructorInfo constructorInfo)
{
	var ctorParameters = constructorInfo.GetParameters();
	var lambdaParameters =
		ctorParameters.Select(x => Expression.Parameter(x.ParameterType, x.Name)).ToArray();
	var argsExp =
		ctorParameters.Select((p, i) => Expression.Convert(lambdaParameters[i], p.ParameterType));
	var newExp = Expression.New(constructorInfo, argsExp);
	var lambda = Expression.Lambda(newExp, lambdaParameters);
	return lambda.Compile();
}

Usage:

CreateDelegate(/* ConstructorInfo */).DynamicInvoke(args);

Solution 9 - C#

My guess is that it isn't possible since you would pass a method of an object that has not been created yet.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionakavelView Question on Stackoverflow
Solution 1 - C#Marc GravellView Answer on Stackoverflow
Solution 2 - C#leppieView Answer on Stackoverflow
Solution 3 - C#Adam RobinsonView Answer on Stackoverflow
Solution 4 - C#Mongus PongView Answer on Stackoverflow
Solution 5 - C#Andrew HareView Answer on Stackoverflow
Solution 6 - C#Kim HomannView Answer on Stackoverflow
Solution 7 - C#Jim FellView Answer on Stackoverflow
Solution 8 - C#shtse8View Answer on Stackoverflow
Solution 9 - C#Serge WautierView Answer on Stackoverflow