c# - threading - parameterizedthreadstart
un constructor como delegado, ¿es posible en C#? (7)
Tengo una clase como la siguiente:
class Foo
{
public Foo(int x) { ... }
}
y necesito pasar a un determinado método un delegado como este:
delegate Foo FooGenerator(int x);
¿Es posible pasar el constructor directamente como un valor FooGenerator
, sin tener que escribir:
delegate(int x) { return new Foo(x); }
?
EDITAR: Para mi uso personal, la pregunta se refiere a .NET 2.0, pero las sugerencias / respuestas para 3.0+ también son bienvenidas.
Creo que tan conciso como vas a obtener (sin moverte a un patrón de fábrica) sería algo con métodos anónimos, como este:
delegate Foo FooGenerator(int x);
...
void DoStuff()
{
YourDelegateConsumer(x => new Foo(x));
}
Esto no está haciendo estrictamente lo que solicitó (ya que está pasando un delegado a un método anónimo que devuelve una nueva instancia, en lugar de un delegado directo al constructor), pero no creo que lo que está pidiendo es estrictamente posible.
Esto es, por supuesto, asumiendo que estás usando 3.5+
Desafortunadamente, los constructores no son exactamente lo mismo que los métodos y, como tal, no puede crear un delegado que los apunte. Sin embargo, esta es una idea interesante, quizás con más información podríamos idear algún tipo de solución que sea sintácticamente similar.
La respuesta de Marc Gravell me inspiró a la siguiente solución muy simple:
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);
}
Casi lo mismo si su constructor tiene parámetros (en este ejemplo: 1 parámetro de tipo 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 });
}
No, el CLR no permite vincular delegados a ConstructorInfo
.
Sin embargo, puedes crear el tuyo:
static T Make<T>(Action<T> init) where T : new()
{
var t = new T();
init(t);
return t;
}
Uso
var t = Make<Foo>( x => { x.Bar = "bar"; x.Baz = 1; });
Parece que probablemente quieras usar el patrón de fábrica de clase.
Supongo que no es posible, ya que pasaría un método de un objeto que aún no se ha creado.
Supongo que normalmente haría algo como esto como parte de una implementación de fábrica, donde los tipos reales no se conocen en tiempo de compilación ...
Primero, tenga en cuenta que un enfoque más sencillo puede ser un paso inicial posterior a la creación, luego puede usar los genéricos:
static T Create<T>({args}) where T : class, ISomeInitInterface, new() {
T t = new T();
t.Init(args);
return t;
}
Luego puedes usar MakeGenericMethod
y / o CreateDelegate
.
De otra manera; puede hacer esto al vuelo con Expression
(3.5) o DynamicMethod
(2.0).
El enfoque de Expression
es más fácil de codificar:
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
o (usando 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