【问题标题】:Observable.FromEvent signatureObservable.FromEvent 签名
【发布时间】:2016-03-08 11:20:32
【问题描述】:

Observable.FromEvent中有这样一个签名的目的是什么? 对于example

var appActivated = Observable.FromEvent(
h => Application.Current.Activated += h,
h => Application.Current.Activated -= h);

具体来说,h 是什么?为什么是+=,然后是-=?我们是从事件还是从事件处理程序创建Observable?如果来自事件,为什么不只是有这样的签名:

var appActivated = Observable.FromEvent(Application.Current.Activated);

【问题讨论】:

  • 我不久前写了一篇关于 FromEvent 的长篇解释和讨论,可能有用:stackoverflow.com/questions/19895373/…
  • @James World:在这里阅读答案后,我有 60-70% 的理解。阅读您的帖子后,它> 95%。特别是您以“请注意,只有调用订阅的行为创建订阅”开头的段落。帮了我很多。
  • 感谢您的反馈。这是一种令人惊讶的深度方法,需要大量解释!

标签: c# system.reactive reactive-programming observable


【解决方案1】:

这是因为没有办法将事件作为参数传递给方法。您可以将事件作为 delegate 传递,但这并不能让您订阅/取消订阅该事件。请参阅 Eric Lippert 的 answer

Observable.From 基本上说“好的,我会给你一个可观察的事件,它是事件的包装器,但你需要为我提供两个委托:1)一个委托,让我订阅我的处理程序到事件,并且2)我需要时取消订阅我的处理程序的代表”。

所以在这种情况下,h => Application.Current.Activated += h 是一个 lambda 表达式,它被编译成一个委托。 h(处理程序)是输入参数,委托接受该输入参数并将其订阅到Activated 事件。第二个委托是同样的事情,除了它取消订阅处理程序。

【讨论】:

    【解决方案2】:

    艾伦的回答是正确的;我想确保您的所有问题都得到解答:

    特别是h是什么?

    h 是添加和删除处理程序的委托的参数。调用时,h 将是对处理程序委托的引用。

    为什么是+=,然后是-=?

    observable 需要能够订阅和取消订阅事件的处理程序。

    我们是从事件还是从事件处理程序创建 Observable?

    来自一个事件。

    如果来自事件,为什么不只是一个签名:var appActivated = Observable.FromEvent(Application.Current.Activated);

    因为这会传递处理程序,而不是事件。 “事件”是三件事:调用处理程序列表的能力、向列表添加新处理程序的能力以及从列表中删除处理程序的能力。 observable 需要最后两个;你的建议是通过第一个。所以 observable 接受最后两个的委托。

    【讨论】:

    • 最后一点,他[大概]没有从定义该事件的类中编写此代码,因此即使访问处理程序列表也是无效的,没有办法编写FromEvent 方法该代码可以为其编译,更不用说做它需要做的事情了。
    • 我实际上仍然不明白的是:我们有事件,我们想从中制作事件流。所以这里没有提到任何处理程序。而这个h 处理程序仿佛不知从何而来。抱歉,我可能在概念上不明白。
    • @Bad:你可能不明白什么是 lambda?当您说customers.Select(c => c.FirstName) 时,c 只是“突然出现”。你明白 lambda 只是编写方法的一种简短方式吗?
    • @Bad:假设我们写了void Adder(EventHandler h) { Activated += h; },然后将new Action<EventHandler>(Adder) 传递给工厂。然后你会说“h 是从哪里来的?”还是现在清楚了? lambda 只是编写该方法的一种简短方式。
    • @Bad:observable 将显示由事件触发产生的一系列值。为此,它需要订阅事件。它是如何做到的? observable 不知道如何订阅事件。你需要告诉它怎么做。所以你给它一个知道怎么做的代表。清楚吗?
    【解决方案3】:

    Observables 是 .NET 中的一等类型 - 这意味着您可以保留对它们的引用并将它们作为参数传递给您喜欢的任何构造函数/方法。

    事件是不是一流的类型。它们只能在您可以引用它们的包含对象的范围内附加和分离。

    所以这意味着我不能这样做:

        public void SomeMethod(EventHandler handler)
        {
            handler += (s, e) => { /* Handler Code */ };
        }
    
        public void SomeOtherMethod()
        {
            SomeMethod(Application.Current.Activated);
        }
    

    如果我尝试得到错误:

    事件 'Application.Activated' 只能出现在 += 或 -= 的左侧

    这应该让你知道为什么你不能这样做var appActivated = Observable.FromEvent(Application.Current.Activated);

    那么,我该如何解决这个问题,以便在 SomeMethod 中附加事件?

    方法如下:

        public void SomeMethod(Action<EventHandler> addHandler)
        {
            addHandler((s, e) => { /* Handler Code */ });
        }
    
        public void SomeOtherMethod()
        {
            SomeMethod(h => Application.Current.Activated += h);
        }
    

    基本上,在SomeMethod方法中,参数不再是EventHandler,而是Action&lt;EventHandler&gt;。这意味着我不再试图传递事件本身 - 相反,我正在传递一种方法让被调用的代码将自身附加到我的事件。对SomeMethod 的调用中的h 是一个承诺,如果将来我有一个有效的处理程序,那么我可以通过调用Action&lt;EventHandler&gt; 来附加它。

    假设我现在想编写一些知道如何附加和分离事件的代码。我现在需要这个代码:

        public void SomeMethod(Action<EventHandler> addHandler, Action<EventHandler> removeHandler)
        {
            EventHandler handler = (s, e) => { /* Handler Code */ };
    
            addHandler(handler);
    
            /* Some Intervening Code */
    
            removeHandler(handler);
        }
    
        public void SomeOtherMethod()
        {
            SomeMethod(h => Application.Current.Activated += h, h => Application.Current.Activated -= h);
        }
    

    /* Some Intervening Code */ 代码中,处理程序被附加,在它被分离之后。

    这将我们带到您问题中的代码:

    var appActivated = Observable.FromEvent(
    h => Application.Current.Activated += h,
    h => Application.Current.Activated -= h);
    

    这与上面的 SomeMethod 调用非常相似 - FromEvent 需要一种方法来附加和分离事件。 h 是一个承诺,上面写着“嘿,FromEvent,如果你能提供一个处理程序,当你将来需要它时,我保证这段代码会正确附加它。”或者,视情况而定。

    现在,为了有点迂腐,您的代码实际上应该是:

            IObservable<EventPattern<EventArgs>> appActivated =
                Observable
                    .FromEventPattern<EventHandler, EventArgs>(
                        h => Application.Current.Activated += h,
                        h => Application.Current.Activated -= h);
    

    现在我有一个IObservable&lt;EventPattern&lt;EventArgs&gt;&gt;,我可以重写SomeMethod,把它作为参数,这样写:

        public IDisposable SomeMethod(IObservable<EventPattern<EventArgs>> appActivated)
        {
            return appActivated.Subscribe(ep => { /* Handler Code */ });
        }
    

    现在可以看到 Rx 的所有功能。 .Subscribe 方法不需要对原始事件的包含对象的任何引用,但它最终会在需要时调用h =&gt; Application.Current.Activated += h 进行附加和h =&gt; Application.Current.Activated -= h 分离。我现在可以有效地将事件作为 .NET 中的一等类型传递。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-25
      • 1970-01-01
      • 2021-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      相关资源
      最近更新 更多