2009-10-21 6 views
51

Ich habe eine Klasse wie unten:ein Konstruktor als Delegierter - ist das in C# möglich?

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

und ich brauche, um eine bestimmte Methode, wie diese einen Delegierten zu übergeben:

delegate Foo FooGenerator(int x); 

Ist es möglich, den Konstruktor direkt als FooGenerator Wert zu übergeben, ohne zu tippen:

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

?

EDIT: Für meinen persönlichen Gebrauch bezieht sich die Frage auf .NET 2.0, aber Hinweise/Antworten für 3.0+ sind ebenfalls willkommen.

+0

Interessante Frage. Ich glaube, Konstrukteure sind in Bezug auf die CLR effektiv Methoden, aber ich würde die Syntax nicht kennen. – Noldorin

+3

Ich bin interessiert: Warum möchten Sie das tun? –

+0

Ich vermute die Antwort ist aber nein. – Noldorin

Antwort

27

Nein, nicht die CLR nicht verbindlich Delegierten ConstructorInfo ermöglichen.

Sie können jedoch nur erstellen Sie Ihre eigenen:

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

Nutzungs

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

Könnten Sie möglicherweise einige Verweise (MSDN link?) Für Ihre Aussage über die Bindung an ConstructorInfo hinzufügen? – akavel

+7

Ein Konstruktor erzeugt kein neues Objekt. Ein Konstruktor arbeitet in Verbindung mit einer Zuweisungsroutine. – user7116

+0

@sixlettervariables: +1 - das sieht nach einer vernünftigen Erklärung aus. Das heißt, ich würde immer noch gerne einige MSDN/C# - Spezifikation/... Referenzen von jemandem sehen. – akavel

2

Leider sind Konstruktoren nicht ganz die gleichen Dinge wie Methoden und als solche können Sie keinen Delegaten erstellen, der auf sie verweist. Dies ist jedoch eine interessante Idee, vielleicht könnten wir mit mehr Informationen eine Art Workaround entwickeln, der syntaktisch ähnlich wäre.

+0

Könnten Sie vielleicht die Aussage "Konstrukteure sind nicht ganz (...) als Methoden" im Kontext von Delegierten erläutern? Ich würde besonders gerne einige Verweise auf MSDN/C# -Referenz/andere Dokumente lieben. – akavel

+0

Nicht reagierende Antwort, die einzige Information ist "Nein" und "Konstruktoren sind nicht ganz die gleichen Dinge wie Methoden" (natürlich nicht). Ja, eine andere Antwort, die vorgibt zu erklären, warum etwas nicht möglich ist, indem sie sagt "es ist einfach nicht". – jwg

5

Es klingt, als ob Sie wahrscheinlich das Klassenfactory-Muster verwenden möchten.

Factory Method Pattern

+0

Das war eigentlich, was ich als Workaround verwendet hatte, aber um die Frage zu schreiben, dachte ich, dass das Konstrukt "delegate" einfacher zu verstehen ist. – akavel

+0

Das Factory Method Pattern ist gut, aber nur, wenn Sie alle möglichen Typen zur Kompilierzeit kennen. –

0

Meine Vermutung ist, dass es nicht möglich ist, da Sie eine Methode eines Objekts passieren würde, die noch nicht geschaffen hat.

7

denke ich so kurz wie Sie bekommen werden (ohne zu einer Fabrik Muster bewegt) wäre etwas mit anonymen Methoden, wie folgt aus:

delegate Foo FooGenerator(int x); 

...  

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

dies nicht unbedingt das tut, was Sie gefragt (Da Sie einen Delegaten an eine anonyme Methode übergeben, die eine neue Instanz anstelle eines direkten Delegaten an den Konstruktor zurückgibt), aber ich denke nicht, wonach Sie fragen, ist absolut möglich.

Dies ist natürlich vorausgesetzt, sind Sie mit 3.5+

+0

+1; Ich kompiliere eigentlich für 2.0 und deshalb musste ich mit "delegate" arbeiten, aber da die Kernfrage etwas anderes ist, sollte das Lambda-Konstrukt sicher in Erinnerung bleiben. – akavel

49

Ich nehme an, Sie würden so etwas wie dies als Teil einer Fabrik Umsetzung in der Regel tun, wo die tatsächlichen Typen aren 't zur Kompilierungszeit bekannt ...

Beachten Sie zuerst, dass ein einfacher Ansatz möglicherweise ein Post-Create-Init-Schritt ist, dann können Sie Generics verwenden:

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

Sie können dann MakeGenericMethod und/oder CreateDelegate verwenden.


Sonst; Sie können dies mit on-the-fly mit Expression (3.5) oder DynamicMethod (2.0) tun.

Der Expression Ansatz ist einfacher 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 

oder (mit 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; technisch, mit Reflektion & c. ist richtig (und ich habe auch einen Moment darüber nachgedacht), aber es hat gravierende Mängel: 1) Wie in Ihrem Kommentar zu sehen ist, tut es der Lesbarkeit schwer zu schaffen (und macht den Code weniger prägnant statt mehr); 2) AFAIK, Reflexion ist langsamer als sprachgestützte Konstrukte, da sie eine weitere Abstraktionsebene stapeln. – akavel

+5

Einmal auf einen Delegaten kompiliert, ist ein reflexionsbasierter Ansatz nicht langsamer und kann (gelegentlich) schneller sein als regulär kompilierter Code. Offensichtlich kompilieren Sie es nur einmal und cachen den Delegierten. –

+3

+1. Dies (der Ausdruck Implementierung) war für mich hilfreicher als die akzeptierte Antwort, da ich den cctor brauchte, nicht den ctor. –

2

Marc GRA Antwort inspiriert mich auf die folgende sehr einfache Lösung:

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

Fast die gleiche Sache, wenn Ihr Konstruktor params hat (in th ist ein Beispiel: 1 param vom Typ 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 }); 
} 
Verwandte Themen