Domanda

Ho una lezione come di seguito:

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

e devo passare a un determinato metodo un delegato come questo:

delegate Foo FooGenerator(int x);

È possibile passare direttamente il costruttore come valore FooGenerator , senza dover digitare:

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

MODIFICA: Per uso personale, la domanda si riferisce a .NET 2.0, ma anche i suggerimenti / risposte per 3.0+ sono i benvenuti.

È stato utile?

Soluzione

No, il CLR non consente ai delegati vincolanti di ConstructorInfo .

Puoi comunque crearne uno tuo:

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

Altri suggerimenti

Suppongo che normalmente faresti qualcosa del genere come parte di un'implementazione di fabbrica, in cui i tipi reali non sono noti al momento della compilazione ...

Innanzitutto, nota che un approccio più semplice può essere un passo di init post-creazione, quindi puoi usare generics:

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

È quindi possibile utilizzare MakeGenericMethod e / o CreateDelegate .


In caso contrario; puoi farlo al volo con Expression (3.5) o DynamicMethod (2.0).

L'approccio Expression è più facile da codificare:

    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

Penso che quanto concederai (senza passare a uno schema di fabbrica) sarebbe qualcosa con metodi anonimi, come questo:

delegate Foo FooGenerator(int x);

...    

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

Questo non sta facendo esattamente quello che hai richiesto (dal momento che stai passando un delegato a un metodo anonimo che restituisce una nuova istanza, piuttosto che un delegato diretto al costruttore), ma non penso a cosa tu ' chiedere è strettamente possibile.

Questo ovviamente presuppone che tu stia utilizzando 3.5+

Sembra che tu voglia utilizzare il modello di classe di fabbrica.

Pattern dei metodi di fabbrica

Sfortunatamente no, i costruttori non sono esattamente le stesse cose dei metodi e come tali non è possibile creare un delegato che li punti. Questa è un'idea interessante però, forse con più informazioni potremmo escogitare una sorta di soluzione alternativa che sarebbe sintatticamente simile.

La risposta di Marc Gravell mi ha ispirato alla seguente soluzione molto semplice:

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

Quasi la stessa cosa se il costruttore ha parametri (in questo esempio: 1 parametro di 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 });
}

La mia ipotesi è che non sia possibile poiché passeresti un metodo di un oggetto che non è stato ancora creato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top