C# 如何使用反应式扩展实现事件
反应式扩展允许您使用C# 如何使用反应式扩展实现事件,c#,events,system.reactive,C#,Events,System.reactive,反应式扩展允许您使用Observable.FromEventPattern轻松订阅事件,但我找不到任何关于当您有IObservable时如何实现事件的信息 我的情况是:我需要实现一个包含事件的接口。该事件应该在对象的某个值更改时调用,出于线程安全原因,我需要在某个SynchronizationContext上调用该事件。我还应该在注册时使用当前值调用每个事件处理程序 public interface IFooWatcher { event FooChangedHandler FooCha
Observable.FromEventPattern
轻松订阅事件,但我找不到任何关于当您有IObservable
时如何实现事件的信息
我的情况是:我需要实现一个包含事件的接口。该事件应该在对象的某个值更改时调用,出于线程安全原因,我需要在某个SynchronizationContext
上调用该事件。我还应该在注册时使用当前值调用每个事件处理程序
public interface IFooWatcher
{
event FooChangedHandler FooChanged;
}
使用Rx,使用行为主体
,获得符合我要求的可观察对象相当容易:
public class FooWatcher
{
private readonly BehaviorSubject<Foo> m_subject;
private readonly IObservable<Foo> m_observable;
public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue)
{
m_subject = new BehaviorSubject<Foo>(initialValue);
m_observable = m_subject
.DistinctUntilChanged()
.ObserveOn(synchronizationContext);
}
public event FooChangedHandler FooChanged
{
add { /* ??? */ }
remove { /* ??? */ }
}
}
但是,我希望找到一个更简单的解决方案,因为我需要实现其中几个事件(不同的处理程序类型)。我试图推出我自己的通用解决方案,但它产生了一些需要解决的其他问题(特别是,如何使用参数为T
)的委托,因此,我更愿意找到一个现有的解决方案来弥补这个方向上的差距,就像EventPattern中的起到了相反的作用一样。您可以这样做:
public event FooChangedHandler FooChanged
{
add { m_observable.ToEvent().OnNext += value; }
remove { m_observable.ToEvent().OnNext -= value; }
}
然而,在删除时,我认为您可能只是想处理订阅。。。或者从ToEvent()获取操作并将其存储为成员。未经测试
编辑:但是,您必须使用Action而不是FoodChangedHandler委托
编辑2:这是一个经过测试的版本。但是,我想您需要使用FoodChangedHandler,因为您有一堆这些预先存在的处理程序
void Main()
{
IObservable<Foo> foos = new [] { new Foo { X = 1 }, new Foo { X = 2 } }.ToObservable();
var watcher = new FooWatcher(SynchronizationContext.Current, new Foo { X = 12 });
watcher.FooChanged += o => o.X.Dump();
foos.Subscribe(watcher.Subject.OnNext);
}
// Define other methods and classes here
//public delegate void FooChangedHandler(Foo foo);
public interface IFooWatcher
{
event Action<Foo> FooChanged;
}
public class Foo {
public int X { get; set; }
}
public class FooWatcher
{
private readonly BehaviorSubject<Foo> m_subject;
public BehaviorSubject<Foo> Subject { get { return m_subject; } }
private readonly IObservable<Foo> m_observable;
public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue)
{
m_subject = new BehaviorSubject<Foo>(initialValue);
m_observable = m_subject
.DistinctUntilChanged();
}
public event Action<Foo> FooChanged
{
add { m_observable.ToEvent().OnNext += value; }
remove { m_observable.ToEvent().OnNext -= value; }
}
}
void Main()
{
IObservable foos=new[]{new Foo{X=1},new Foo{X=2}}.ToObservable();
var-watcher=newfoowatter(SynchronizationContext.Current,newfoo{X=12});
watcher.FooChanged+=o=>o.X.Dump();
订阅(watcher.Subject.OnNext);
}
//在此处定义其他方法和类
//公共代表无效FooChangedHandler(Foo-Foo);
公共接口IFooWatcher
{
事件行为发生变化;
}
公开课Foo{
公共整数X{get;set;}
}
公共类食品观察者
{
私有只读行为主体m_subject;
公共行为主体{get{return m_Subject;}}
私有只读IOobservable m_observable;
公共FooWatcher(SynchronizationContext SynchronizationContext,Foo initialValue)
{
m_subject=新行为主体(初始值);
m_可观察=m_受试者
.DistinctUntilChanged();
}
公众活动行动计划
{
添加{m_observable.ToEvent().OnNext+=value;}
删除{m_observable.ToEvent().OnNext-=value;}
}
}
考虑到您已经在混合反应性代码和更普通的代码之间的界限,您可以做一个反应性更小的版本。要开始,只需声明一个普通事件模式
public event FooChangedHandler FooChanged;
protected void OnFooChanged(Foo)
{
var temp = FooChanged;
if (temp != null)
{
temp(new FooChangedEventArgs(Foo));
}
}
然后在构造器中简单地将可观察对象连接到它
m_Observable.Subscribe(foo => OnFooChanged(foo));
它不是很简单,但非常简单。谢谢,我以前没有看过ToEvent()
!这已经是谜题的一大块了。Action
和FooChangedHandler
之间的转换仍然是一个问题(是的,不幸的是,我们一直在处理这些问题)。问题:如果代理具有相同的目标和方法,则它们是相等的,但这种相等仅达到一个级别-如果您有一个代理并将其包装在两个单独的操作
实例中,则它们将相等,但如果您将这两个操作
实例包装在另一个操作
中,则它们将不相等。然而,我想我已经看到了解决这个问题的方法……啊,是的,这里是:我明天会测试这个,再次感谢!现在一切正常。使用类似上面链接的解决方案将代理转换为操作
,可以让我编写一个简短的通用解决方案,在内部使用ToEvent()
。该类没有太多内容,但我想将主题
和IEventSource
捆绑在一个对象中。很抱歉再次收回accept,但我注意到您的示例代码存在严重问题。调用ToEvent
将始终创建一个新的IEventSource
,因此您的代码将尝试从其注册的不同事件中注销。我试过了,但肯定不行。调用ToEvent
一次并存储结果。一旦你解决了这个问题,我会重新接受的。好吧,酷,我今天晚些时候/今晚试着去做。。。我今天有考试。干杯。您可以在一开始就为FooChanged
分配一个delegate{}
,将此解决方案折叠为两行,从而完全省去OnFooChanged
。但是,这种方法不符合我的要求,因为事件的新订阅者不接收当前状态。此外,由于mySyncrhonizationContext
本质上是发布到事件循环,因此新订阅者可以接收在订阅建立之前发生的更改(使用Java内存模型术语)。这可能不是问题,但我宁愿避免它。
m_Observable.Subscribe(foo => OnFooChanged(foo));