Ungebundenes delegate



  • Moin,

    Ich suche etwas wie ein delegate ohne sofort eine Instanz daran zu binden. Also etwas wie ein Methodenzeiger, dem ich erst beim Invoke die Instanz übergebe. Ich habe es jetzt mit Reflection lösen können, frage mich aber ob es auch anders geht.
    Ein Beispiel:

    class Item
    {
        public void SetF(int f)
        {
            F = f;
        }
        public int F { get; set; }
    }
    
    class Builder<T> where T : new()
    {
        private MethodInfo method;
        private int i = 0;
    
        public Builder(string methodName)
        {
            Method(methodName);
        }
    
        public void Property(string propertyName)
        {
            method = typeof(T).GetProperty(propertyName).GetSetMethod();
        }
    
        public void Method(string methodName)
        {
            method = typeof(T).GetMethod(methodName);
        }
    
        public T Build()
        {
            T t = new T();
            method.Invoke(t, new object[] { i++ });
            return t;
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Builder<Item> b = new Builder<Item>("SetF");
            Item i1 = b.Build();
            b.Property("F");
            Item i2 = b.Build();
        }
    }
    


  • Wie wär es mit einem Mediator?



  • Du meinst das Mediator Pattern? Versteh ich jetzt in diesem Zusammenhang überhaupt nicht (Mediator ist allerdings auch eines der mir weniger geläufigen Muster).
    Die Frage ist aber auch eher technisch gemeint. Also gibt es da irgendeinen build-in Mechanismus.



  • Dann hab ich deine Anforderung evtl nicht richtig verstanden.
    Mit dem Mediator kann man halt Methoden in Objekten aufrufen lassen ohne das man die Instanzen kennen muss.



  • David W schrieb:

    Dann hab ich deine Anforderung evtl nicht richtig verstanden.

    Ich hatte gehofft das geht aus meinem Beispiel hervor. Vielleicht ncohmal einfacher:

    static void Main(string[] args)
        {
            Item i = new Item();
            F(i.SetF);  // das geht. Das i ist gebunden
    
            // Sowas aber will ich. Nur die Methode, ohne gebundene Instanz
            // Geht aber nicht
            Item i2 = F(Item.SetF);
        }
    
        static void F(Action<int> f)
        {
            f(42);
        }
    
        static Item F(GesuchtesDingens f)
        {
            Item i = new Item();
            f(i, 42); // so
            i.f(42)   // oder so, oder...?
            return i;
        }
    

    Eigentlich wie ein Methodenzeiger in C++.



  • Das geht wenn Du SetF static machst. Kommt das in Frage?



  • Achso, jetzt versteh ich.

    Wie wäre es damit:

    static void Main(string[] args)
    {
        F((item, value) => Adjust(item, 42));
    }
    
    static void Adjust(Item item, int f)
    {
        item.SetF(f);
    }
    
    static Item F(Action<Item, int> f)
    {
        Item i = new Item();
        f(i, 42);
        return i;
    }
    

    Oder kürzer:

    static void Main(string[] args)
    {
        F((item, value) => item.SetF(value));
    }
    
    static Item F(Action<Item, int> f)
    {
        Item i = new Item();
        f(i, 42);
        return i;
    }
    


  • µ schrieb:

    Das geht wenn Du SetF static machst. Kommt das in Frage?

    Nein, leider nicht.

    David W schrieb:

    Wie wäre es damit:

    static void Main(string[] args)
    {
        F((item, value) => item.SetF(value));
    }
    
    static Item F(Action<Item, int> f)
    {
        Item i = new Item();
        f(i, 42);
        return i;
    }
    

    Das würde funktionieren.

    class Item
    {
        public string First { get; set; }
        public string Second { get; set; }
    }
    
    class Builder<T> where T : new()
    {
        private List<Action<T, string>> methods = new List<Action<T,string>>();
    
        public Builder(params Action<T, string>[] setter)
        {
            methods.AddRange(setter);
        }
    
        public List<T> Build()
        {
            List<T> result = new List<T>();
            for (int i = 0; i < 3; ++i)
            {
                T item = new T();
                foreach (Action<T, string> set in methods)
                {
                    string s = Console.ReadLine();
                    set(item, s);
                }
                result.Add(item);
            }
            return result;
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Builder<Item> b = new Builder<Item>(
                (item, value) => item.First = value,
                (item, value) => item.Second = value);
    
            Item i = b.Build()[0];
        }
    }
    

    Find ich allerdings nicht so besonders elegant.. Naja scheint wohl nicht anders zu gehen.


  • Administrator

    Was genau willst du machen? Vielleicht können wir dir besser helfen, wenn wir wissen, was du schlussendlich willst. Sag uns nicht womit du es machen willst sondern warum 😉

    Z.B. bei deinem letzten Beispiel, könnte man dies genauso über ein Interface erledigen:

    interface ITextInitializable
    {
      void Initialize(string[] lines);
    }
    

    (Über die Namen darf man streiten 🤡)
    Beim Konstruktor von Builder die Anzahl Linien übergeben, T muss ITextInitializable implementieren und in Initialize kann das Objekt sich dann selber initialisieren.

    Grüssli



  • Dravere schrieb:

    Was genau willst du machen?

    Genau das wollte ich auch gerade fragen.
    Das sieht nämlich danach aus das eine Factory eventuell die bessere Lösung wäre.



  • Es geht um eine Klasse (man nenne sie XYZBuilder, XYZFactory oder was auch immer), die aus einer CSV Datei irgendwelche Instanzen irgendwelcher Klassen erzeugt. Jede Zeile, eine Instanz. Dabei kann der Benutzer zur Laufzeit definieren, welche Spalte der CSV Datei welcher Bedeutung hat. Diese "Regel" kann man dann abspeichern und wieder verwenden.
    Das ganze habe ich wie gessagt mit Reflection schon recht einfach gelöst. Dachte es geht viellciht ähnlich einfach auch ohne.
    Das ganze sieht dann etwa so aus.

    personen.csv
    
    Hans,23,Musterstr. 27
    Jürgen,65,Schlossalle 1
    
    class Person
    {
      public string Name{get;set;}
      public string Adresse{get;set;}
    }
    
    CSVRule rule = new CSVRule().Property("Name").Ignore().Property("Adresse");
    CSVBuilder<Person> builder(rule);
    List<Person> personen = builder.Build(new StreamReader("personen.csv"));
    
    class CSVBuilder
    {
      public List<T> Build(TextReader reader)
      {
        List<T> result = ...
        CSVStream csv = new CSVStream(reader);
        while(!csv.EndOfStream)
        {
          List<string> row = csv.NextRow();
          T t = new T();
          for(int i = 0; i < rule.Count; ++i)
             rule[i].Invoke(t, row[i]);
          result.Add(t);
        }
        return result;
      }
    }
    

    Irgendwie so.. Was ich tatsächlch geschrieben hab, hab ich grad nicht da, aber so ähnlich.
    Man kann natürlich noch irgendwelche Klassen dazuerfinden, oder der Person Klasse statische Methoden verpassen, aber ich wollte es einfach halten und die Person Klasse nicht anpassen müssen.


Anmelden zum Antworten