2009-10-21 11 views
51

tengo una clase, como a continuación:un constructor como delegado: ¿es posible en C#?

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

y necesito para pasar a un determinado método de un delegado de esta manera:

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); } 

?

EDIT: Para mi uso personal, la pregunta se refiere a .NET 2.0, pero las sugerencias/respuestas para 3.0+ también son bienvenidas.

+0

Interesante pregunta. Creo que los constructores son métodos efectivos en lo que respecta al CLR, pero no sabría la sintaxis. – Noldorin

+3

Estoy interesado: ¿por qué querrías hacer eso? –

+0

Sospecho que la respuesta es sin embargo. – Noldorin

Respuesta

27

No, el CLR no permite que los delegados de unión a ConstructorInfo.

Sin embargo, puede simplemente crear su propia:

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; }); 
+0

¿Podría posiblemente agregar algunas referencias (enlace de MSDN?) Para su declaración sobre el enlace a ConstructorInfo? – akavel

+7

Un constructor no produce un objeto nuevo. Un Constructor funciona junto con una rutina de asignación. – user7116

+0

@sixlettervariables: +1: parece una explicación razonable. Dicho esto, todavía me gustaría ver algunas referencias de MSDN/C# -specification/... de alguien. – akavel

2

Desafortunadamente no, los constructores no son exactamente las mismas cosas que los métodos y, por lo tanto, no se puede crear un delegado que los señale. 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.

+0

¿Podría explicar la afirmación de que "los constructores no son (...) como métodos" en el contexto de los delegados? Me encantaría especialmente algunas referencias a MSDN/C# reference/other docs. – akavel

+0

Respuesta insensible, la única información provista es 'No' y 'los constructores no son exactamente las mismas cosas que los métodos' (por supuesto que no). Sí, otra respuesta que pretende explicar por qué algo no es posible diciendo "simplemente no lo es". – jwg

5

Parece que probablemente desee utilizar el patrón de fábrica de clase.

Factory Method Pattern

+0

Eso es realmente lo que había usado como una solución, pero para escribir la pregunta, pensé que la construcción 'delegado' era más fácil de entender. – akavel

+0

El patrón de método de fábrica es bueno, pero solo si conoce todos los tipos posibles en tiempo de compilación. –

0

Mi conjetura es que no es posible ya que pasaría un método de un objeto que no se ha creado todavía.

7

creo que lo más conciso que va a conseguir (sin mover a un patrón de fábrica) sería algo con los métodos anónimos, así:

delegate Foo FooGenerator(int x); 

...  

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

Esto no es hacer estrictamente lo que pediste (dado 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 sea estrictamente posible.

Esto es, por supuesto, asumiendo que usted está utilizando 3.5 +

+0

+1; En realidad estoy compilando para 2.0, y es por eso que tuve que trabajar con "delegado", pero como la pregunta central es sobre otra cosa, la construcción lambda seguramente debería ser recordada. – akavel

49

Asumo que normalmente haría algo como esto como parte de una implementación de fábrica, donde los tipos reales aren 't conocido en tiempo de compilación ...

En primer lugar, tenga en cuenta que un enfoque más fácil puede ser un paso de inicialización posterior, luego puede utilizar los genéricos:

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

Puede usar MakeGenericMethod y/o CreateDelegate.


De lo contrario; puede hacerlo sobre la marcha con Expression (3.5) o DynamicMethod (2.0).

El Expression enfoque es más fácil de código:

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 
+1

Uh oh; técnicamente, usando la reflexión & c. es correcto (y también lo pensé un momento), pero tiene serios defectos: 1) como se ve en tu comentario, daña seriamente la legibilidad (y hace que el código sea menos conciso en vez de más); 2) AFAIK, la reflexión es más lenta que las construcciones soportadas por el lenguaje, ya que acumula un nivel más de abstracción. – akavel

+5

Una vez compilado para un delegado, un enfoque basado en la reflexión no es más lento y puede (en ocasiones) ser más rápido que el código compilado normal. Obviamente, solo lo compila una vez y almacena en caché al delegado. –

+3

+1. Esto (la implementación de la expresión) fue más útil para mí que la respuesta aceptada, ya que necesitaba el cctor, no el ctor. –

2
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 params (en es un ejemplo: 1 param del 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 }); 
} 
Cuestiones relacionadas