Ссылка делегата действия в другом классе? - PullRequest
1 голос
/ 13 марта 2019

Я ищу способ разрешить другому классу добавлять методы в мой делегат Action, вызывая метод из этого класса, чем вызывая Action для первого класса.

Это то, что мне нужно:

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

        ProgramTest prog = new ProgramTest(ref Execute);

        prog.AddMethod();

        Execute();
    }
}

class ProgramTest
{
    public Action execute;

    public ProgramTest(ref Action action)
    {
        execute = action;
    }

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

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

Однако, когда я вызываю Execute (), ничего не происходит.

Как мне заставить это работать?

Ответы [ 4 ]

1 голос
/ 13 марта 2019

Другой вариант - поместить (неизменяемый) делегат в изменяемый контейнер.

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();
    }
}
0 голосов
/ 13 марта 2019

Вы можете заставить его работать, вызвав prog.Execute вместо Execute, как показано ниже.

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

или вам нужно назначить метод Print основному методу Execute делегата, как показано ниже

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

        Execute += prog.Print;

        prog.AddMethod();

        Execute();

    }
}
0 голосов
/ 13 марта 2019

Ваш Program может предоставить событие, для которого ваш другой класс может зарегистрировать другой обработчик:

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();
    }
}
0 голосов
/ 13 марта 2019

То, что вы хотите, это:

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

Это печатает test на консоли.


Это немного лучшая версия этого паттерна:

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

Я бы даже пошел на шаг дальше и определил бы MetaAction - вы можете передавать его сколько угодно и добавлять к нему методы.

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