67

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.

akavel
  • 4,789
  • 1
  • 35
  • 66
  • Interesting question. I believe constructors are effectively methods as far as the CLR is concerned, but I wouldn't know the syntax. – Noldorin Oct 21 '09 at 13:06
  • 3
    I'm interested: why would you want to do that? – Mitch Wheat Oct 21 '09 at 13:07
  • I suspect the answer is no however. – Noldorin Oct 21 '09 at 13:07
  • @Mitch Wheat: in more details: 1) I had classes 'Deriv1' and 'Deriv2' extending 'Foo', 2) I had a method 'SomeLogic()' which sometimes needs to create a 'Foo'-derived object based on 'x', 3) and I wanted to decide whether this should be 'Deriv1' or 'Deriv2' when calling the method. Now that I think of this, I might want to consider using a template method... but for that to be an answer, I should rather open a separate question :) +1 & kudos to you anyway :) – akavel Oct 21 '09 at 15:09

9 Answers9

61

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
ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    Uh oh; technically, using reflection &c. is correct (and I did also think about it for a moment), but it has serious flaws: 1) as seen in your comment, it seriously hurts readability (and makes the code less concise instead of more); 2) AFAIK, reflection is slower than language-supported constructs, as it stacks one more level of abstraction. – akavel Oct 21 '09 at 15:59
  • 7
    Once compiled to a delegate, a reflection based approach is no slower, and can (on occasion) be faster, than regular compiled code. Obviously you only compile it once and cache the delegate. – Marc Gravell Oct 21 '09 at 17:35
  • 4
    +1. This (the expression implementation) was more helpful to me than the accepted answer, since I needed the cctor, not the ctor. – Merlyn Morgan-Graham Jun 17 '11 at 01:12
  • 1
    +1 This is obviously better than the accepted answer! Thanks! – Venemo May 23 '12 at 09:58
  • 1
    Excellent answer but I can't help not saying "ouch" when in Java you can do `Function ctor = Foo::new`. And please don't take me wrong, I **loathe** Java. – Stelios Adamantidis Apr 22 '20 at 19:13
37

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; });
leppie
  • 115,091
  • 17
  • 196
  • 297
  • Could you possibly add some references (MSDN link?) for your statement about binding to ConstructorInfo? – akavel Oct 21 '09 at 14:52
  • 10
    A constructor does not produce a new object. A Constructor works in conjunction with an allocation routine. – user7116 Oct 21 '09 at 15:42
  • @sixlettervariables: +1 - that looks like a reasonable explanation. That said, I'd still love to see some MSDN/C#-specification/... references from someone. – akavel Oct 21 '09 at 16:07
  • @akavel: Do I need to? http://msdn.microsoft.com/en-us/library/system.delegate.createdelegate.aspx, dig deeper in Reflector to see that requires a RuntimeMethodHandle. – leppie Oct 21 '09 at 17:46
  • `ConstructorInfo` != `MethodInfo`, hence a delegate cannot be created from a `ConstructorInfo` (as it isn't a Method). – user7116 Oct 21 '09 at 18:47
  • Accepting the response for the CLR-related information & comments. Thankies to all! – akavel Oct 24 '09 at 11:12
  • 3
    Another, possibly dodgy, solution is to mirror all your ctors via a set of `public static Create(/* args */)` methods. Hence you can say `Func = Foo.CreateFoo`. – Jonathan Dickinson Feb 14 '12 at 11:50
  • @JonathanDickinson - however, that would have to be done for *every* "class Foo" that you want to use in this way. So, every time you think "now I want to use this technique for `class SomeClass`, you then have to either add such a method to that class definition, or add an extension method in a static class. I'm not seeing the circumstances under which your suggestion would be preferable to leppie's answer, which "just works", leveraging the compiler's inference engine. Usage `Foo.CreateFoo` not enough easier than `Make`. – ToolmakerSteve Feb 04 '17 at 03:41
  • @JonathanDickinson ... on the other hand, what you suggest *is* closer to the usage form that OP asked for, so kudos for that! – ToolmakerSteve Feb 04 '17 at 03:46
10

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+

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • +1; I'm actually compiling for 2.0, and that's why I had to work with "delegate", but as the core question is about something else, the lambda construct should surely be remembered. – akavel Oct 21 '09 at 14:31
  • Yes, I think this is the most elegant and least verbose way to do it in "modern" C#. Tested it, works perfectly! +1 – SBS Jan 02 '21 at 22:03
6

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

Factory Method Pattern

Mongus Pong
  • 11,337
  • 9
  • 44
  • 72
  • That's actually what I had used as a workaround, but for writing the question I thought the 'delegate' construct to be easier to understand. – akavel Oct 21 '09 at 14:56
  • The Factory Method Pattern is good, but only if you know all possible types at compile-time. – Kim Homann Jan 12 '16 at 16:00
2

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.

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
  • Could you possibly elaborate on the statement that "constructors are not quite (...) as methods" in context of delegates? I'd love especially some references to MSDN/C# reference/other docs. – akavel Oct 21 '09 at 15:44
  • Unresponsive answer, the only information provided is 'No' and 'constructors are not quite the same things as methods' (of course not). Yes another answer that pretends to explain why something isn't possible by saying "it just isn't". – jwg Mar 28 '13 at 10:22
2

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 });
}
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Kim Homann
  • 3,042
  • 1
  • 17
  • 20
1

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);
Jim Fell
  • 13,750
  • 36
  • 127
  • 202
1

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);
shtse8
  • 1,092
  • 12
  • 20
-1

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

Serge Wautier
  • 21,494
  • 13
  • 69
  • 110