Question

J'ai une classe comme ci-dessous:

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

et j'ai besoin de passer à une certaine méthode un délégué comme celui-ci:

delegate Foo FooGenerator(int x);

Est-il possible de passer le constructeur directement en tant que valeur FooGenerator , sans avoir à taper:

delegate(int x) { return new Foo(x); }

?

EDIT: Pour mon usage personnel, la question fait référence à .NET 2.0, mais les astuces / réponses pour la version 3.0+ sont également les bienvenues.

Était-ce utile?

La solution

Non, le CLR n'autorise pas les délégués de liaison à ConstructorInfo .

Vous pouvez cependant créer le vôtre:

static T Make<T>(Action<T> init) where T : new()
{
  var t = new T();
  init(t);
  return t;
}

Utilisation

var t = Make<Foo>( x => { x.Bar = "bar"; x.Baz = 1; });

Autres conseils

Je suppose que vous feriez normalement quelque chose comme ceci dans le cadre d'une implémentation en usine, où les types réels ne sont pas connus au moment de la compilation ...

Tout d’abord, notez qu’une approche plus simple peut être une étape d’initialisation post-création, puis que vous pouvez utiliser des génériques:

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

Vous pouvez ensuite utiliser MakeGenericMethod et / ou CreateDelegate .

Sinon; Vous pouvez le faire à la volée avec Expression (3.5) ou DynamicMethod (2.0).

L'approche Expression est plus facile à coder:

    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

ou (en utilisant 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

Je pense que le plus concis possible (sans passer à un modèle d'usine) serait quelque chose utilisant des méthodes anonymes, comme ceci:

delegate Foo FooGenerator(int x);

...    

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

Cela ne fait pas strictement ce que vous avez demandé (puisque vous passez un délégué à une méthode anonyme qui renvoie une nouvelle instance, plutôt qu'un délégué direct au constructeur), mais je ne pense pas à quoi vous ' Il est strictement possible de demander.

Ceci, bien sûr, en supposant que vous utilisiez la version 3.5 +

Il semble que vous souhaitiez probablement utiliser le modèle d'usine de classe.

Modèle de méthode d'usine

Malheureusement non, les constructeurs ne sont pas tout à fait les mêmes que les méthodes et vous ne pouvez donc pas créer de délégué qui les pointe. C’est une idée intéressante, mais avec plus d’informations, nous pourrions concevoir une solution de contournement dont la syntaxe serait similaire.

La réponse de Marc Gravell m'a inspiré la solution très simple suivante:

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

Presque la même chose si votre constructeur a des paramètres (dans cet exemple: 1 paramètre de 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 });
}

Je suppose que ce n'est pas possible car vous transmettriez une méthode d'objet qui n'a pas encore été créé.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top