1
votes

Référence de délégué d'action dans une autre classe?

Je cherche un moyen d'autoriser une autre classe à ajouter des méthodes à mon délégué Action en invoquant une méthode de cette classe, plutôt qu'en appelant l'Action sur la première classe.

Voici ce dont j'ai besoin:

< pre> XXX

Cependant, lorsque j'appelle Execute (), rien ne se passe.

Comment puis-je le faire fonctionner?


1 commentaires

Vous ne pouvez pas - pas comme ça. Les délégués sont des types immuables - le + = crée un nouveau délégué. Le fait que vous passiez ref Action action ne fait rien, car le champ execute n'est pas ref (et ne peut pas non plus l'être)


4 Réponses :


0
votes

Ce que vous voulez, c'est ceci:

class Program
{
    static void Main(string[] args)
    {
        Action Execute = delegate { };

        MetaAction meta = MetaAction.Create(h => Execute += h, h => Execute -= h);

        var prog = new ProgramTest(meta);

        var subscription = prog.AddMethod();

        Execute();

        subscription.Dispose();
    }
}

public class MetaAction
{
    public static MetaAction Create(Action<Action> attach, Action<Action> detach)
        => new MetaAction(attach, detach);

    public Action<Action> _attach;
    public Action<Action> _detach;

    private MetaAction(Action<Action> attach, Action<Action> detach)
    {
        _attach = attach;
        _detach = detach;
    }

    public IDisposable Subscribe(Action action)
    {
        _attach(action);
        return Disposable.Create(() => _detach(action));
    }
}

public class ProgramTest
{
    public MetaAction _meta;

    public ProgramTest(MetaAction meta)
    {
        _meta = meta;
    }

    public IDisposable AddMethod()
    {
        return _meta.Subscribe(Print);
    }

    public void Print()
    {
        Console.WriteLine("test");
        Console.ReadLine();
    }
}

public sealed class Disposable : IDisposable
{
    public static IDisposable Create(Action action)
        => new Disposable(action);

    private readonly Action _action;
    private int _disposed;

    private Disposable(Action action)
    {
        _action = action;
    }

    public void Dispose()
    {
        if (Interlocked.Exchange(ref _disposed, 1) == 0)
        {
            _action();
        }
    }
}

Cela imprime test sur la console.


Il s'agit d'une version légèrement meilleure de ce modèle:

class Program
{
    static void Main(string[] args)
    {
        Action Execute = delegate { };

        ProgramTest prog = new ProgramTest(h => Execute += h, h => Execute -= h);

        var subscription = prog.AddMethod();

        Execute();

        subscription.Dispose();
    }
}

class ProgramTest
{
    public Action<Action> _attach;
    public Action<Action> _detach;

    public ProgramTest(Action<Action> attach, Action<Action> detach)
    {
        _attach = attach;
        _detach = detach;
    }

    public IDisposable AddMethod()
    {
        _attach(Print);
        return Disposable.Create(() => _detach(Print));
    }

    public void Print()
    {
        Console.WriteLine("test");
        Console.ReadLine();
    }
}

public sealed class Disposable : IDisposable
{
    public static IDisposable Create(Action action)
        => new Disposable(action);

    private readonly Action _action;
    private int _disposed;

    private Disposable(Action action)
    {
        _action = action;
    }

    public void Dispose()
    {
        if (Interlocked.Exchange(ref _disposed, 1) == 0)
        {
            _action();
        }
    }
}

J'irais même plus loin et définirais une MetaAction - vous pouvez transmettre autant que vous aimez et ajoutez-y des méthodes.

class Program
{
    static void Main(string[] args)
    {
        Action Execute = delegate { };

        ProgramTest prog = new ProgramTest(h => Execute += h);

        prog.AddMethod();

        Execute();
    }
}

class ProgramTest
{
    public Action<Action> execute;

    public ProgramTest(Action<Action> action)
    {
        execute = action;
    }

    public void AddMethod()
    {
        execute(Print);
    }

    public void Print()
    {
        Console.WriteLine("test");
        Console.ReadLine();
    }
}


2 commentaires

Ça a marché! Merci! Mais comment puis-je supprimer et ajouter de nouveaux appels de méthode au délégué Action?


@JohnnyB - Vous ne pouvez pas. Faire h => Execute + = h, h => Execute - = h est la seule façon que cela fonctionne.



1
votes

Une autre option consiste à placer le délégué (immuable) dans un conteneur mutable.

public class ActionContainer
{
    public Action Action { get; set; } = () => { };
}

class Program
{
    static void Main(string[] args)
    {
        ActionContainer execute = new ActionContainer();

        ProgramTest prog = new ProgramTest(execute);

        prog.AddMethod();

        execute.Action();
    }
}

class ProgramTest
{
    public ActionContainer execute;

    public ProgramTest(ActionContainer action)
    {
        execute = action;
    }

    public void AddMethod()
    {
        execute.Action += Print;
    }

    public void Print()
    {
        Console.WriteLine("test");
        Console.ReadLine();
    }
}


1 commentaires

Génial, très simple et fait le travail. Me permet de jouer avec le délégué Action n'importe où. Merci!



-1
votes

Votre Programme peut exposer un événement auquel votre autre classe peut enregistrer un autre gestionnaire:

class Program
{
    public static event Action MyEvent;
    static void Main(string[] args)
    {
        ProgramTest prog = new ProgramTest();

        prog.AddMethod();

        // raise the event and invoke the registered handlers
        MyEvent?.Invoke();
    }
}

class ProgramTest
{
    private Action handler;

    public ProgramTest()
    {
        handler = Print;
    }

    public void AddMethod()
    {
        Program.MyEvent += handler;  // regsiter the execute-delegate to the event
        // or directly: Program.MyEvent += Print;
    }

    public void Print()
    {
        Console.WriteLine("test");
        Console.ReadLine();
    }
}


1 commentaires

J'ai l'impression que vous ne l'avez peut-être pas testé avant de poster.



0
votes

Vous pouvez le faire fonctionner en appelant prog.Execute au lieu de Execute, tout comme le code ci-dessous.

class Program
{
    static void Main(string[] args)
    {
        Action Execute = delegate { };            
        ProgramTest prog = new ProgramTest(ref Execute);

        Execute += prog.Print;

        prog.AddMethod();

        Execute();

    }
}

ou vous devez attribuer la méthode Print à la méthode principale Exécuter le délégué comme ci-dessous

class Program
{
    static void Main(string[] args)
    {
        Action Execute = delegate { };
        ProgramTest prog = new ProgramTest(ref Execute);
        prog.AddMethod();
        prog.execute();
    }
}


1 commentaires

Merci pour votre réponse. Mais j'ai plusieurs classes différentes qui ajouteront des méthodes à ce délégué. La classe principale elle-même ne sait pas quand cela se produira et quels objets le feront.