首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >跟踪Action<T> lambdas as Action<object>

跟踪Action<T> lambdas as Action<object>
EN

Stack Overflow用户
提问于 2020-07-22 09:46:55
回答 1查看 63关注 0票数 0

过去提出的类似问题如下:

都找到了将Action<T> myActionT包装成匿名new Action<object>(o => myActionT((T)o));的解决方案。

我的用例是,初始的、类型化的lambda是一个数据订阅回调,将其封装到一个匿名的回调中,意味着我失去了使用原始Action<T>取消订阅的能力。

我把它缩小到了一个样板例子:

代码语言:javascript
复制
    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 (因为实际上我只需要值类型)

是否有更好/更优雅的方法来正确跟踪通用Action回调?

稍后编辑:添加关于我的用例的更多细节:

updates

  • clients可以订阅字段,并可以选择接收从字符串转换到其他类型的值(我正在使用TypeDescriptor.GetConverter(type).ConvertFromString(fieldValue)处理转换)

例如:

代码语言:javascript
复制
 Subscribe<decimal>("price", (decimal px) => { /* do smthg with price*/ })
 Subscribe<bool>("opened", (bool opened) => { ... })
 Subscribe<string>("code", (string code) => { ... })

我觉得我现在的方法(函数指针/代理,跟踪订阅类型)很难做到,但我没有找到更简单的方法来实现这一点(除了用我需要的类型重载Subscribe之外,这可能是一个明智的吻选择)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-07-22 09:52:28

太简单了。试一试:

代码语言:javascript
复制
private List<Delegate> callbacks = new List<Delegate>();

C#编译器允许您将任何Action<T>转换为Delegate。您的代码编译并运行良好。

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

代码语言:javascript
复制
IDisposable Subscribe<T>(Action<T> cbk)

然后您就不需要Unsubscribe方法了--这将要求您保持对Action<T> cbk的引用。相反,您只需在订阅时调用.Dispose()

尝试下面的代码:

代码语言:javascript
复制
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();
        }
    }
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63031399

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档