Reputation: 4875
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.
Upvotes: 68
Views: 28884
Reputation: 1365
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);
Upvotes: 1
Reputation: 14256
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);
Upvotes: 1
Reputation: 1062745
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
Upvotes: 62
Reputation: 3229
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 });
}
Upvotes: 2
Reputation: 117220
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; });
Upvotes: 37
Reputation: 185643
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+
Upvotes: 10
Reputation: 21878
My guess is that it isn't possible since you would pass a method of an object that has not been created yet.
Upvotes: 0
Reputation: 11477
It sounds like you probably want to be using the class factory pattern.
Upvotes: 6
Reputation: 351506
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.
Upvotes: 2