Zum Inhalt springen
  • 0

C# Methode aus delegat entfernen


MaceFan

Frage

'nabend die Gemeinde.


 

using System;

namespace EinEinfacherDelegateBool_v001
{
    class Program
    {
        class Caller
        {
            public delegate bool OnAction();
            // public delegate bool RefOnAction(ref TypeOneWorker typeOneWorker);

            public OnAction DoAction;
            // public RefOnAction refDoAction;

            public bool CallDoAction()
            {
                if (this.DoAction != null)
                {
                    return this.DoAction();
                }
                return false;
            }

            //public bool refCallDoAction()
            //{
            //    if (this.refDoAction != null)
            //    {
            //        return this.refCallDoAction();
            //    }
            //    return false;
            //}
        }

        class TypeOneWorker
        {
            static int id = 0;
            int autoid = -1;
            public bool Action()
            {
                id++;
                autoid = id;
                Console.WriteLine("TypeOneWorker {0}.", autoid);
                //Console.ReadKey();
                return true;
            }
        }

        class TypeTwoWorker
        {
            public bool Action()
            {
                Console.WriteLine("TypeTwoWorker.");
                //Console.ReadKey();
                return false;
            }
        }

        static void Main(string[] args)
        {
            Caller c = new Caller();
            TypeOneWorker typeOneWorker = new TypeOneWorker();

            c.DoAction = new Caller.OnAction(typeOneWorker.Action);
            c.DoAction += new Caller.OnAction(new TypeTwoWorker().Action);
            c.DoAction += new Caller.OnAction(typeOneWorker.Action);
            c.DoAction += new Caller.OnAction(new TypeTwoWorker().Action);

            //
            // ... more 'n more
            //
            // c.DoAction = new Caller.OnAction(typeOneWorker.Action);
            // c.DoAction += new Caller.OnAction(new TypeTwoWorker().Action);
            //
            // c.DoAction -= BaWorker.Action;
            //

            c.CallDoAction();

            object x = c.DoAction.GetInvocationList();
            Console.Write("\n");
            Console.Write("End M(ain).");
            Console.ReadKey();
        }
    }
}

 

Das Ganze ist Teil einer Mess-Steuer-Softare (Sensor / Ventil) die als multithreading umgesetzt weden soll.

Wie bekomme ich nun bei einer beliebig langen Liste an Methoden-Aufrufen von Caller.DoAction eine bestimmte Methode wieder entfernt, wenn ich nicht weiss an welcher Stelle in der Liste / welches "Element" in der Liste diese Methode ist?

Ich kann mir zwar per < c.DoAction.GetInvocationList() > die Liste abrufen. Ich kann auch per < c.DoAction -= typeOneWorker.Action > wieder eine Methode entfernen. Der delegate arbeitet dabei aber wie ein Stack - und entfernt das letzte, passende, Element in der Liste. Ich wuerd nun aber gern ein bestimmtes Element / eine bestimmte Methode entfernen, ohne zu wissen an welcher Stelle.

 

Hm... wer weiss Rat?

 

huebschen Abend wuensch ich,

 

mf

Bearbeitet von MaceFan
Link zu diesem Kommentar
Auf anderen Seiten teilen

6 Antworten auf diese Frage

Empfohlene Beiträge

  • 1

Die Delegates sind ja keine Zauberei. Die sind lediglich nur eine Liste von Funktionszeigern. Eine Methode ist im Grunde ja auch nichts weiter als eine Referenz auf einen Speicherbereich.

Ich verstehe aber immer noch nicht so ganz, wieso du dies löschen möchtest. Ich habe da nur ein Verdacht, wieso du das tun möchtest aber wenn ich richtig liege, dann muss ich dir sagen, dass die Delegates dafür nicht gedacht sind. Ich vermute mal, dass du die aufzurufenden Methoden per Multithreading abarbeiten möchtest und daher mehrere Instanzen von der Caller-Klasse erzeugst, die alle die gleichen Methodenaufrufe bekommen und du dann die herauslöschen möchtest, du dann schon aufgerufen wurden. Liege ich da richtig?

Dafür sind Delegates aber nicht gedacht. Delegates werden einfach in einem Thread sequentiell abgearbeitet. Wenn du nebenläufige Aufgaben erledigen möchtest, gibt es die Klasse Task<T>, die solche darstellen und ausgeführt werden. Mit den Methoden Task.WhenAll() bzw. Task.WhenAny() kann man dann auf die Ergebnisse der Tasks warten:

class Program
{
  public static async Task Main(string[] args)
  {
    var tasks = new Task<(string, bool)>[]
    {
      new Task<(string, bool)>(() =>
      {
        Console.WriteLine("Start Task 1");
        Thread.Sleep(1000);
        Console.WriteLine("End Task 1");
        return ("Task 1", true);
      }),
      new Task<(string, bool)>(() =>
      {
        Console.WriteLine("Start Task 2");
        Thread.Sleep(100);
        Console.WriteLine("End Task 2");
        return ("Task 2", false);
      })
    };
    
    foreach (var task in tasks)
      task.Start();
    
    foreach (var result in await Task.WhenAll(tasks))
      Console.WriteLine($"{result.Item1} => {result.Item2}");
  }
}

Das Threadhandling übernimmt dann die CLR bzw. das .NET Framework für uns.

Dein Beispiel mit dem Bescheidsager und Ausführer lässt sich auch eleganter lösen, denn im Grunde ist dies das Decorator Pattern. Du schreibst ein Interface, was sowohl dein Bescheidsager, als auch dein Ausführer implementiert. Im Konstruktor vom Bescheidsager gibst du dann eine Instanz vom Ausführer mit, die dann nicht null sein darf. Dann sparst du dir auch dieses lästige if-then-else bei der Ausführung.

interface IAusfuehrer
{
  bool Ausfuehren();
}

class Bescheidsager : IAusfuehrer
{
  private readonly IAusfuehrer _ausfuehrer;
  
  public Bescheidsager(IAusfuehrer ausfuehrer)
  {
    _ausfuehrer = ausfuehrer ?? throw new ArgumentNullException(nameof(ausfuehrer));
  }

  public bool Ausfuehren()
  {
    Console.WriteLine("Starte Methode");
    return _ausfuehrer.Ausfuehren();
  }
}

class Ausfuehrer : IAusfuehrer
{
  public bool Ausfuehren()
  {
    Console.WriteLine("Ausfuehrer wird ausgeführt");
    return true;
  }
}

class Program
{
  public static void Main(string[] args)
  {
    var ausfuehrer = new Ausfuehrer();
    var bescheidsager = new Bescheidsager(ausfuehrer);
    
    Console.WriteLine(bescheidsager.Ausfuehren());
  }
}
Bearbeitet von Whiz-zarD
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Und woher weiß du, welches du entfernen möchtest? An irgendwas muss man ja das Objekt erkennen können. Sei es auch nur der Klassentyp. Dann muss man halt selber die Methode in der Invocationlist finden.

Aber das ist doch recht verquirlt. Die sollen ja auch aus irgendeinen Grund entfernt werden. Wäre es dann nicht sinnvoller, bei der Registrierung zu prüfen, ob die Methode aufgerufen werden soll? Dann spart man sich das Entfernen und kann jede Methode einzeln steuern. Also sowas wie:

if(...)
  c.DoAction + = ...
if(...)
  c.DoAction + = ...
if(...)
  c.DoAction + = ...

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Es ist "uninteressant" woher ich weiss welches ich entfernen moechte, ich weiss es eben.

Was ist "verquirlt"? Denk Dir einfach das auskommentierte weg - dann wird's klarer  :)

Ja, es hat seinen Grund dass sie wieder entfernt werden sollen (die Methoden) - sie wurden ausgefuehrt.

Und wenn ich ein IF-Construckt analog Deinem verwende... und irgendwann eine (bestimmte) registrierte Methode wieder loswerden will (von der ich nicht weiss an welcher Stelle in der InvocationList diese steht), dann passiert das genau wie? Ich sehe hier denselben Schuh, nur in anderer Farbe.

 

Ich haett da schon eine gewisse Vorstellung, wie ich mir 'ne "delegate-Klasse" zusammen bastel die meine Wuensche erfuellt. Nur, warum das Problem neu erfinden, wenn ich es denn mit vorhandenen Mitteln erschlagen kann?

Bearbeitet von MaceFan
Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

Wie wärs wenn du auf anonyme Methoden verzichtest :

            var meth1 = new Caller.OnAction(typeOneWorker.Action);
            var meth2 = new Caller.OnAction(new TypeTwoWorker().Action);
            var meth3 = new Caller.OnAction(typeOneWorker.Action);
            var meth4 = new Caller.OnAction(new TypeTwoWorker().Action);

            var methoden = meth1 + meth2 + meth3 + meth4;

            c.DoAction = methoden;

            c.CallDoAction(); // Alle ausgeführt

            //Entferne meth2, meth4
            c.DoAction = methoden - meth4 - meth2;

            Console.WriteLine("Nur meth1 und meth3 ausgeführt:");
            c.CallDoAction();

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0
vor 2 Stunden schrieb ihkaka:

Wie wärs wenn du auf anonyme Methoden verzichtest :

Ich mag die, ebenso wie delagate's, (eigentlich) auch nicht - ist mir zuviel "Zauberei" im Spiel. 's war zum testen halt... sie waren ploetzlich da... es fuehlte sich so toll an. K.A. wie ich mich dazu hab hinreissen koennen o)

Ich finde den Ansatz von Dir aber auch recht interessant.

Habe alternativ an sowas gedacht:

class Bescheidsager
        {
            public Ausfuehrer ausfuehrer;

            public void FuehreAus()
            {
                Console.WriteLine("Ueberpruefung auf vorhandene Referenz auf <<<Ausfuehrer>>> wird gestartet...");
                if (ausfuehrer != null)
                {
                    Console.WriteLine("Ueberpruefung erfolgreich. Starte Methode auf <<<Ausfuehrer>>>.");
                    ausfuehrer.IchWerdeAusgefuehrt();
                }
                else
                {
                    Console.WriteLine("Ueberpruefung negativ. Es wird keine Methode ausgefuehrt.");
                }
            }
        }

        class Ausfuehrer
        {
            public void IchWerdeAusgefuehrt()
            {
                Console.WriteLine("<<<IchWerdeAusgefuehrt>>> in <<<Ausfuehrer>> wird von <<<Bescheidsager>>> zur Ausfuehrung gebracht.");
            }
        }

        static void Main(string[] args)
        {
            Console.Clear();
            Console.WriteLine("Hello World!");
            Bescheidsager bescheidsager = new Bescheidsager();
            bescheidsager.ausfuehrer = new Ausfuehrer();
            bescheidsager.FuehreAus();
            Console.ReadKey();
        }

<Ausfuehrer> dabei als Array oder Liste verwaltet... 's erscheint mir einfacher nutzbar zu sein als mein jetziges Vorgehen.

Ich werd, so die Arbeitslast es zulaesst, mal drueber nachdenken und rumfummeln.

 

uebschen tag wuensch ich

 

mf

Link zu diesem Kommentar
Auf anderen Seiten teilen

  • 0

  

  

Am 11.2.2022 um 07:16 schrieb Whiz-zarD:

...

Nun ja. Ich habe eine "Sensor-Class". Die misst irgendwas (was ist unwichtig) und die "interessanten" Werte koennen per Get-Methode abgerufen werden - aber der Sensor misst eben nur und stellt die Daten zur Verfuegung. Weiterhin habe ich eine Klasse, welche gewisse/bestimmte Aktionen ausfuehren soll (einen Stromkreisauf/Ventil/Motor oeffnen/schliessen/aktivieren/deaktivieren etc. Von diesen Klassen koennen (letztendlich) beliebig viele Instanzen erzeugt werden die in einer 1:1 - Beziehung stehen (optional 1:m, m:1 und m:n).

Die einzelnen Instanzen der Klassen werden jeweils in einer Liste vom Typ List<Sensor> bzw. List<Ventil> verwaltet, die in Beziehung stehenden Klassen (Sensor/Ventil) durch ein Struct-Constuckt, das die entsprechenden Referenzen auf die jeweiligen Instanzen beinhaltet. Die einzelnen Instanzen (Sensor/Ventil) laufen jeweils in einem eigenstaendigen Thread (wobei das Ventil nicht in einem eigenen Thread laufen muss - 's reicht ja, wenn es auf Anforderung (oeffnen/schliessen) reagiert).

Eine weitere Klasse stellt die Logik im Zusammenspiel von Sensor/Ventil da. Werden z.B. bestimmte Werte erreicht, ueberschritten, unterschritten etc., soll (einmalig) eine Reaktion durch z.B. das Ventil erfolgen. Die entsprechenden Methoden (von Ventil) sind (derzeit) parameterlose void-Methoden. Und da war die Idee das mit Delegaten zu probieren/umzusetzen - die entsprechende Delegate-Liste soll (periodisch/regelmaessig) durchlaufen werden. Und wenn das Ventil geoeffnet/geschlossen wurde... muss es ja nicht wieder geoeffnet/geschlossen werden - deshalb will ich die entsprechende Methode wieder "loeschen".

Im "Bescheidsager-Ausfuehrer" - Modell (ohne Delegate's) in Listen verwaltet funktioniert das schon ganz huebsch.

Das ganze ist eine "EIGENE, auf meinem Mist gewachsene" Erweiterung eines in meinem (fuer IT-Verhaeltnisse sehr, sehr alten (methusalix)) "Lehrbuch" verwendeten Beispiel - als naechstes werd ich mir das dann per "Event" angucken und rumprobieren.

 

...ich verlier gedanklich grad ein wenig den Faden. Deshalb huebsches WE an dieser Stelle

 

mf

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Diese Frage beantworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...