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

9

68

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.

Karinkarina answered 21/10, 2009 at 13:4 Comment(4)
Interesting question. I believe constructors are effectively methods as far as the CLR is concerned, but I wouldn't know the syntax.Renfro
I'm interested: why would you want to do that?Ruthy
I suspect the answer is no however.Renfro
@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 :)Karinkarina
U
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; });
Unfailing answered 21/10, 2009 at 13:15 Comment(9)
Could you possibly add some references (MSDN link?) for your statement about binding to ConstructorInfo?Karinkarina
A constructor does not produce a new object. A Constructor works in conjunction with an allocation routine.Holton
@sixlettervariables: +1 - that looks like a reasonable explanation. That said, I'd still love to see some MSDN/C#-specification/... references from someone.Karinkarina
@akavel: Do I need to? msdn.microsoft.com/en-us/library/…, dig deeper in Reflector to see that requires a RuntimeMethodHandle.Unfailing
ConstructorInfo != MethodInfo, hence a delegate cannot be created from a ConstructorInfo (as it isn't a Method).Holton
Accepting the response for the CLR-related information & comments. Thankies to all!Karinkarina
Another, possibly dodgy, solution is to mirror all your ctors via a set of public static <SELF> Create<SELF>(/* args */) methods. Hence you can say Func<string, string, Foo> = Foo.CreateFoo.Yes
@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<Foo>.Playhouse
@JonathanDickinson ... on the other hand, what you suggest is closer to the usage form that OP asked for, so kudos for that!Playhouse
O
62

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
October answered 21/10, 2009 at 15:35 Comment(5)
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.Karinkarina
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.October
+1. This (the expression implementation) was more helpful to me than the accepted answer, since I needed the cctor, not the ctor.Dizon
+1 This is obviously better than the accepted answer! Thanks!Brokendown
Excellent answer but I can't help not saying "ouch" when in Java you can do Function<Integer, Foo> ctor = Foo::new. And please don't take me wrong, I loathe Java.Vansickle
U
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; });
Unfailing answered 21/10, 2009 at 13:15 Comment(9)
Could you possibly add some references (MSDN link?) for your statement about binding to ConstructorInfo?Karinkarina
A constructor does not produce a new object. A Constructor works in conjunction with an allocation routine.Holton
@sixlettervariables: +1 - that looks like a reasonable explanation. That said, I'd still love to see some MSDN/C#-specification/... references from someone.Karinkarina
@akavel: Do I need to? msdn.microsoft.com/en-us/library/…, dig deeper in Reflector to see that requires a RuntimeMethodHandle.Unfailing
ConstructorInfo != MethodInfo, hence a delegate cannot be created from a ConstructorInfo (as it isn't a Method).Holton
Accepting the response for the CLR-related information & comments. Thankies to all!Karinkarina
Another, possibly dodgy, solution is to mirror all your ctors via a set of public static <SELF> Create<SELF>(/* args */) methods. Hence you can say Func<string, string, Foo> = Foo.CreateFoo.Yes
@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<Foo>.Playhouse
@JonathanDickinson ... on the other hand, what you suggest is closer to the usage form that OP asked for, so kudos for that!Playhouse
J
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+

Jabot answered 21/10, 2009 at 13:15 Comment(2)
+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.Karinkarina
Yes, I think this is the most elegant and least verbose way to do it in "modern" C#. Tested it, works perfectly! +1Madra
C
6

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

Factory Method Pattern

Carnallite answered 21/10, 2009 at 13:10 Comment(2)
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.Karinkarina
The Factory Method Pattern is good, but only if you know all possible types at compile-time.Contumely
C
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.

Costate answered 21/10, 2009 at 13:7 Comment(2)
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.Karinkarina
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".Materials
C
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 });
}
Contumely answered 11/1, 2016 at 13:5 Comment(0)
S
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);
Sexennial answered 23/9, 2019 at 17:52 Comment(0)
B
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);
Burkhard answered 18/3, 2021 at 9:49 Comment(0)
H
0

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

Hernia answered 21/10, 2009 at 13:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.