Gute Idee gesucht für automatische generische Delegate Erzeugung


  • Administrator

    Hallo zusammen,

    Ich wollte vor kurzem das folgende hinbekommen:

    public class SomeHelper
    {
      public void SomeObject Create<TResult>(Func<TResult> func)
      {
        return new SomeObjectImpl<TResult>(func);
      }
    
      public void SomeObject Create<T1, TResult>(Func<T1, TResult> func)
      {
        return new SomeObjectImpl<T1, TResult>(func);
      }
    
      public void SomeObject Create<T1, T2, TResult>(Func<T1, T2, TResult> func)
      {
        return new SomeObjectImpl<T1, T2, TResult>(func);
      }
    
      // etc.
    }
    
    class AnotherThing
    {
      public int Foo(int x)
      {
        return x;
      }
    
      public void Bar()
      {
        var obj = SomeHelper.Create(this.Foo);
      }
    }
    

    Das funktioniert aber nicht, weil der C# 4.0 Kompiler hier die Typeparameter nicht herausfinden kann. Ich möchte aber gerne sowas in die Richtung erreichen, da es sonst einfach nur mühsam ist, die Klassen zu verwenden:

    public void Bar()
    {
      var obj = new SomeObjectImpl<int, int>(new Func<int, int>(this.Foo));
    }
    

    Man gibt eigentlich die Information doppelt und dreifach an.

    Hat vielleicht einer eine gute Idee, wie man das vereinfachen könnte?

    Danke im Voraus.

    Grüssli



  • Folgende Varianten sollten doch auch funktionieren?

    // Voraussetzung Create() ist static
    public void Bar()
    {
      var obj = SomeHelper.Create<int, int>(Foo);
    }
    
    // Voraussetzung Create ist nicht static
    public void Bar()
    {
      var obj = new SomeHelper().Create<int, int>(Foo);
    }
    

    Das ist zwar immer noch nicht das was du möchtest, aber schon mal die Richtung, das das new Func<...>() ja nicht nötig ist.


  • Administrator

    @PuppetMaster2k,
    Ja das geht. Aber ist trotzdem noch Redundanz drin. Ich würde das irgendwie gerne komplett wegbekommen.

    Als zusätzliche Hilfestellung vielleicht:
    Ich bin am Ende eigentlich nur noch an der Basisklasse Delegate interessiert, bzw. sogar Object . Heisst ich brauche nie die Delegates in ihren Spezialisierungen selber. Am Ende wird das alles über Reflection verwendet, bzw. die Funktionen aufgerufen. Es geht wirklich nur um die Erstellung.

    Grüssli



  • Dann stellt sich die Frage ob du mit dem generischen Ansatz noch weiterkommst. Reicht es dir vielleicht, ein Delegate als Parameter zu verwenden und die Erzeugung der Klassen per Reflection zu lösen? Alles andere scheint mir ziemlich viel Aufwand zu sein.


  • Administrator

    PuppetMaster2k schrieb:

    Dann stellt sich die Frage ob du mit dem generischen Ansatz noch weiterkommst. Reicht es dir vielleicht, ein Delegate als Parameter zu verwenden und die Erzeugung der Klassen per Reflection zu lösen? Alles andere scheint mir ziemlich viel Aufwand zu sein.

    Daran bin ich aktuell auch am überlegen. Ich gehe sogar soweit, ob nicht allenfalls auch Attributes und co eine sinnvolle Sache wäre. Ich glaube, dass ich mir den Teil nochmals durchdenken muss. Danke.

    Grüssli



  • @Dravere
    Mal ne doofe Frage, aber...
    Was für nen Typ hat eigentlich der Ausdruck " this.Foo "?

    Bzw. auch: weisst du wieso der Compiler die Typen nicht herleiten kann?



  • Ich hab mich noch nicht weiter damit beschäftigt, aber vielleicht helfen ja die neuen Varianz-Keywords "in" und "out" von C# 4.0:

    http://msdn.microsoft.com/de-de/library/vstudio/dd233060.aspx


  • Administrator

    hustbaer schrieb:

    Bzw. auch: weisst du wieso der Compiler die Typen nicht herleiten kann?

    Ja, ich vermute es. Ich hatte einfach gehofft gehabt, dass der Compiler so klug ist, zu erkennen, dass this.Foo eine eindeutig Funktion ist, welche nicht überladen wurde, und dadurch automatisch ein Delegate daraus erstellt und dieses Objekt der Create-Funktion übergibt. Theoretisch wäre das möglich...

    @loks,
    Nein, ich glaube nicht, dass hier Varianzen helfen. Schon nur wegen dem, weil man diese nicht auf Typparameter von Funktionen anwenden kann. Ist nur bei Delegates und Interfaces anwendbar.

    Grüssli



  • Ah, Überladung, OK. Daran hatte ich mal wieder nicht gedacht 🙂
    Hmpf.

    Wenn der Standard nicht vorsieht dass hier bei Overload-Sets die bloss aus einer einzigen Methode bestehen eine Spezialbehandlung passiert, dann wird es wohl einfach nicht gehen.

    Über Reflection sollte es aber mMn. möglich sein.
    Ist zwar nicht sehr Performant, aber wenn das bei deiner Anwendung keine Rolle spielt...

    Dass die Implementierung nicht sehr elegant ist, sollte ja egal sein. Beim Aufruf merkt man davon ja nichts, das wäre also zweitrangig.


  • Administrator

    hustbaer schrieb:

    Ist zwar nicht sehr Performant, aber wenn das bei deiner Anwendung keine Rolle spielt...

    Dass die Implementierung nicht sehr elegant ist, sollte ja egal sein. Beim Aufruf merkt man davon ja nichts, das wäre also zweitrangig.

    Die Performance spielt in der Tat keine Rolle. Ich wollte einfach nur erreichen, dass man beliebige Funktionen einfach und schnell einbinden kann.

    Mal sehen. Vielleicht kommt mir heute Abend noch eine zündende Idee. 🤡

    Ansonsten werde ich es wohl so lassen, wie es PuppetMaster2k beschrieben hat.

    Danke.

    Grüssli



  • hustbaer schrieb:

    Was für nen Typ hat eigentlich der Ausdruck " this.Foo "?

    Method-Group ist ein compilerinterner Typ ohne Entsprechung in C#. Man kann eine Method-Group keiner Variablen zuweisen, nur einem Delegate unter Angabe eines exakten Typs. An irgendeiner Stelle muss die Signatur wohl immer angegeben werden und deshalb bin ich mir relativ sicher, dass es keine Lösung für Draveres Problem gibt.



  • Hm.
    Das heisst mit

    x => this.Foo(x)
    

    statt

    this.Foo
    

    müsste es gehen, right?



  • Das Problem besteht weiter hin. x ist immer noch unbekannt und du wirst genötigt die Signatur zu spezifizieren.

    Folgendes funktioniert hingegen wieder, weil x als int definiert wurde:

    (int x) => Foo(x)
    

    Leider macht es das nicht besser.



  • Achja, stimmt. *hand-auf-stirn-patsch*

    Die Sache mit "ich muss keinen Typ angeben" geht ja auch nur weil der aus dem Kontext hergeleitet wird in dem man das Lambda verwendet. Und genau den gibt es in diesem Fall ja nicht.



  • Eigentlich sollte es kein Problem sein dem Compiler beizubringen, sowas zu akzeptieren: Delegate d = this.Foo;
    Delegate ist von Natur aus Multicastfähig, warum also nicht die ganze Method-Group aller Überladungen von Foo daran binden? Imho wäre das für Fälle wie diesen ganz nützlich, weil man dann einen Typ in C# hätte, der mit Method-Groups umgehen kann.




  • Wie soll das gehen? Die Methoden in der Method-Group haben doch alle unterschiedliche Signaturen.
    (OK, müssen nicht, aber normalerweise ist es so)
    Wie willst du das aufrufen wenn der erste Foo Overload nen int als Parameter nimmt un der zweite drei Strings?



  • Genauso wie es jetzt schon der Fall ist:
    public virtual Delegate[] GetInvocationList()
    und dann darüber iterieren und
    public Object DynamicInvoke(object[] args)
    aufrufen.

    Das kann ja auch mit Reflection gekoppelt werden.
    Was ich sagen will: Delegate scheint mir der naheliegende und natürliche Typ für eine Method-Group zu sein und ich sehe keinen zwingenden Grund, warum der Compiler das nicht kann.


Anmelden zum Antworten