跟踪操作<T> lambda 作为操作<object>

Track Action<T> lambdas as Action<object>

过去问过的类似问题:

全部解决了将 Action<T> myActionT 包装成匿名 new Action<object>(o => myActionT((T)o));
我的用例是初始的类型化 lambda 是一个数据订阅回调,将其包装成一个匿名回调意味着我失去了使用原始 Action<T>

取消订阅的能力

我将范围缩小到这个样板示例:

    internal class Demo
    {
        public class Dispatcher
        {
            private List<Action<object>> callbacks = new List<Action<object>>();

            public void Subscribe<T>(Action<T> cbk)
            {
                callbacks.Add(cbk); // <- compile error, Action<T> is contravariant
            }
            
            public void Unsubscribe<T>(Action<T> cbck)
            {
                callbacks.Remove(cbck); // <- compile error
            }
        }

        private static void Handler(int v) {}
        private static void Handler(string v) {}

        public static void Main()
        {
            var dispatcher = new Dispatcher();
            dispatcher.Subscribe<int>(Handler);
            dispatcher.Subscribe<string>(Handler);

            dispatcher.Unsubscribe<int>(Handler);
        }
    }

当然我可以重载Subscribe(因为我实际上只需要值类型)
是否有 better/more 优雅的方式来正确跟踪通用 Action 回调?

稍后编辑:添加有关我的用例的更多详细信息:

例如:

 Subscribe<decimal>("price", (decimal px) => { /* do smthg with price*/ })
 Subscribe<bool>("opened", (bool opened) => { ... })
 Subscribe<string>("code", (string code) => { ... })

我觉得我目前的方法(函数指针/委托,跟踪订阅类型)陷入困境 但我没有找到更直接的方法来实现这一点(除了用我需要的类型重载 Subscribe,这可能是明智的 KISS 选择)

超级简单。试试这个:

private List<Delegate> callbacks = new List<Delegate>();

C# 编译器允许您将任何 Action<T> 转换为 Delegate。您的代码可以通过该更改正常编译和运行。


但是,我建议将您的 Subscribe 方法签名更改为:

IDisposable Subscribe<T>(Action<T> cbk)

那么您就不需要 Unsubscribe 方法 - 这需要您保留对 Action<T> cbk 的引用。相反,您只需在订阅上调用 .Dispose()

试试这个代码:

internal class Demo
{
    public class Dispatcher
    {
        private List<Delegate> callbacks = new List<Delegate>();

        public IDisposable Subscribe<T>(Action<T> cbk)
        {
            callbacks.Add(cbk);
            return new AnonymousDisposable(() => callbacks.Remove(cbk));
        }
    }

    private static void Handler(int v) { }
    private static void Handler(string v) { }

    public static void Main()
    {
        var dispatcher = new Dispatcher();
        var subscription = dispatcher.Subscribe<int>(Handler);
        dispatcher.Subscribe<string>(Handler);

        subscription.Dispose();
    }
}

public sealed class AnonymousDisposable : IDisposable
{
    private readonly Action _action;
    private int _disposed;

    public AnonymousDisposable(Action action)
    {
        _action = action;
    }

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