这就是我的意思。假设我正在使用公开事件的API,但这些事件不符合标准EventHandler
或EventHandler<TEventArgs>
签名。一个事件可能看起来像这样,例如:
Public Event Update(ByVal sender As BaseSubscription, ByVal e As BaseEvent)
现在,通常情况下,如果我想从某个活动获得IObservable<TEventArgs>
,我可以这样做:
Dim updates = Observable.FromEvent(Of UpdateEventArgs)( _
target:=updateSource, _
eventName:="Update" _
)
但是这不起作用,因为Update
事件不是EventHandler<UpdateEventArgs>
- 事实上, 没有UpdateEventArgs
- 它基本上是只是它自己的东西。
显然,我可以定义我的自己的类派生自EventArgs
(即UpdateEventArgs
),编写另一个类来包装提供Update
事件的对象给包装类提供自己的 Update
事件 EventHandler<UpdateEventArgs>
,并从获得IObservable<UpdateEventArgs>
< / em>的。但这是一项烦人的工作。
有没有办法从像这样的“非标准”事件中创建IObservable<[something]>
,还是我运气不好?
更新:从Jon Skeet的回答中,我向Observable.FromEvent
的以下重载方向轻推:
Function FromEvent(Of TDelegate, TEventArgs As EventArgs)( _
conversion As Func(Of EventHandler(Of TEventArgs), TDelegate), _
addHandler As Action(Of TDelegate), _
removeHandler As Action(Of TDelegate) _
) As IObservable(Of IEvent(Of TEventArgs))
但我必须承认,我在绕Func(Of EventHandler(Of TEventArgs), TDelegate)
部分缠身时遇到了麻烦。它似乎倒退了我(?)。显然,我只是缺少一些......
无论如何,如果它有帮助,我认为这就是等效的C#代码的样子(我会非常诚实:我不确定这一点。即使我通常更喜欢C#我自己,这段代码是我的一位同事的作品,他主要用VB.NET编写; VB.NET允许多种语法来声明事件):
// notice: not an EventHandler<TEventArgs>
public delegate void UpdateEventHandler(BaseSubscription sender, BaseEvent e);
// not 100% sure why he did it this way
public event UpdateEventHandler Update;
这里棘手的部分是,似乎某种类派生自EventArgs
的类是必要的,无论如何。在我正在使用的API中,没有这样的类。所以,最低限度,我必须写一个。但这应该是相当微不足道的(基本上是一个属性:BaseEvent
)。
最后,我假设这个重载所需的代码在C#中看起来像这样:
var updates = Observable.FromEvent<UpdateEventHandler, UpdateEventArgs>(
// conversion (Func<EventHandler<UpdateEventArgs>, UpdateEventHandler>)
handler => (sender, e) => handler(sender, new UpdateEventArgs(e)),
// addHandler (Action<UpdateEventHandler>)
handler => updateSource.Update += handler,
// removeHandler (Action<UpdateEventHandler>)
handler => updateSource.Update -= handler
);
首先:我是否直截了当?第二个问题:我是否正确地说使用VB 9,如果不编写自己的方法,就没有办法完成上述工作?
我几乎觉得我从完全错误的角度开始接近这个问题。但我真的不确定。
答案 0 :(得分:3)
也许您可以为自定义事件签名添加自己的实现?
public interface ICustomEvent<TSource, TArgs>
{
public TSource Source { get; }
public TArgs EventArgs { get; }
}
public interface CustomEvent<TSource, TArgs> : ICustomEvent<TSource, TArgs>
{
public TSource Source { get; set; }
public TArgs EventArgs { get; set; }
}
public static class ObservableEx
{
public static IObservable<ICustomEvent<TSource, TArgs>> FromCustomEvent(
Action<Action<TSource, TArgs>> addHandler,
Action<Action<TSource, TArgs>> removeHandler)
{
return Observable.CreateWithDisposable(observer =>
{
Action<TSource, TArgs> eventHandler = (s,a) =>
observer.OnNext(new CustomEvent<TSource,TArgs>(s,a));
addHandler(eventHandler);
return Disposable.Create(() => removeHandler(eventHandler));
});
}
}
然后您可以将其用作:
var observable = ObservableEx.FromCustomEvent<BaseSubscription,BaseEvent>(
h => updateSource.Update += h,
h => updateSource.Update -= h
);
答案 1 :(得分:1)
您可以能够使用此签名:
Public Shared Function FromEvent(Of TDelegate, TEventArgs As EventArgs) ( _
conversion As Func(Of EventHandler(Of TEventArgs), TDelegate), _
addHandler As Action(Of TDelegate), _
removeHandler As Action(Of TDelegate) _
) As IObservable(Of IEvent(Of TEventArgs))
在这里,TDelegate
将是事件的委托类型(我不能立即从你的声明中说出来 - C#事件声明看起来不那样,我恐怕不会知道足够的VB去decypher它,但我确定那里有一个委托类型)。 TEventArgs
将是事件参数的类型(我认为BaseEvent
应该在这里执行)。您需要提供从EventHandler(Of BaseEvent)
到委托类型的转换器 - 这可能只是一个lambda表达式,用于传递给它的参数来调用给定的事件处理程序。添加和删除操作将是正常的事件订阅代码 - 但表示为代理。
不幸的是,我的VB不够好,不能很好地表达所有这些 - 或者确实知道在VB 9或10中有多少容易获得。我知道如何在C#中看到它...如果你能给我C#中的一个简短但完整的例子让我填写订阅位,我当然可以这样做......
答案 2 :(得分:1)
如果updateSource永远不会消失,你也可以以懒惰的方式做到这一点:
var observable = new Subject<BaseEvent>();
updateSource.Update += (o,e) => observable.OnNext(e);
但Jon的计划可能更好,但受试者可以帮助你。
答案 3 :(得分:1)
为了将来参考,这是使用FromEvent的转换重载的示例,使用FileSystemEventHandler作为示例:
Dim createWatcher As New FileSystemWatcher With {.Path = "C:\Temp", .EnableRaisingEvents = True}
Dim foo = Observable.FromEvent(Of FileSystemEventHandler, FileSystemEventArgs)(
Function(ev) New FileSystemEventHandler(Sub(o, e) ev(o, e)),
Sub(ev) AddHandler createWatcher.Created, ev,
Sub(ev) RemoveHandler createWatcher.Created, ev)
Dim changedEv = Observable.FromEvent(Of FileSystemEventHandler, FileSystemEventArgs)(
Function(ev) New FileSystemEventHandler(Sub(o, e) ev(o, e)),
Sub(ev) AddHandler createWatcher.Changed, ev,
Sub(ev) RemoveHandler createWatcher.Changed, ev)
foo.Subscribe(Sub(e) Console.WriteLine("File {0} created.", e.EventArgs.Name))
changedEv.Subscribe(Sub(e) Console.WriteLine("File {0} changed.", e.EventArgs.Name))
答案 4 :(得分:0)
var updates = Observable.FromEvent<UpdateEventHandler, UpdateEventArgs>(
// conversion (Func<EventHandler<UpdateEventArgs>, UpdateEventHandler>)
handler => (BaseSubscription sender, BaseEvent e) => handler.Invoke(sender, new UpdateEventArgs(e)),
// addHandler (Action<UpdateEventHandler>)
handler => updateSource.Update += handler,
// removeHandler (Action<UpdateEventHandler>)
handler => updateSource.Update -= handler
);
确保UpdateEventArgs ctor接受BaseEvent参数。
答案 5 :(得分:0)
我有一个类似的噩梦,因为我正在使用一个使用非标准.net事件的互操作API。我是很多东西的初学者,包括Generics,Funcs,Actions,Observables和Rx,所以我相信我对如何理解这些东西的经验会有一些价值。
我们可以通过了解它的使用位置来了解Func(Of EventHandler(Of TEventArgs), TDelegate) conversion
。
但首先我们必须了解FromEvent
方法的泛型签名。
下面是vb中的FromEvent扩展方法的函数签名:
Function FromEvent(Of TDelegate, TEventArgs As EventArgs)( _
conversion As Func(Of EventHandler(Of TEventArgs), TDelegate), _
addHandler As Action(Of TDelegate), _
removeHandler As Action(Of TDelegate) _
) As IObservable(Of IEvent(Of TEventArgs))
但我并不真正使用vb所以我将不得不求助于c#签名:
IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(
Func<Action<TEventArgs>, TDelegate> conversion,
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler);
让我们逐行分析c#签名。
注意:有时我会在lambda表达式中包含数据类型,以区分标准.net事件和非标准事件。
第一部分:
IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(
这表示函数FromEvent接受TDelegate
和TEventArgs As EventArgs
作为INPUT。请注意,OUTPUT IObservable
的类型也为TEventArgs
所以当你说你需要一个包含TDelegate
数据的类时,你是对的。我不知道我正在使用什么版本但它允许我使用任何类,即使它不从EventArgs
继承。如果vb不允许你,那么无论如何它都是微不足道的工作,因为它只是将:EventArgs
附加到类(继承自vb?)。让我们将其应用于您的问题:
你的c#假设:
// notice: not an EventHandler<TEventArgs> public delegate void
public delegate void UpdateEventHandler(BaseSubscription sender, BaseEvent e);
// class to wrap the data from the above delegate
public class UpdateEventArgs:EventArgs {...}
申请第一行成为:
var updates = FromEvent<UpdateEventHandler, UpdateEventArgs>(
第二部分:
接下来,我们有三个输入conversion
,addhandler
和removehandler
:
Func<Action<TEventArgs>, TDelegate> conversion,
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler);
我们知道addHandler
和removeHandler
只是添加和删除事件中的委托。我们先做两个。
// addHandler (Action<UpdateEventHandler>)
handler => updateSource.Update += handler,
// removeHandler (Action<UpdateEventHandler>)
handler => updateSource.Update -= handler
现在让我们将类型应用于棘手的部分:
Func<Action<UpdateEventArgs>, UpdateEventHandler> conversion,
此函数将Action<UpdateEventArgs>
作为输入,UpdateEventHandler
委托是输出。我们将它分配给一个名为conversion
Func<Action<UpdateEventArgs>, UpdateEventHandler> conversion
= (handlerOfUpdateEventArgs) =>
{
UpdateEventHandler handler = (BaseSubscription sender, BaseEvent e) =>
handlerOfUpdateEventArgs(sender, new UpdateEventArgs(e));
return handler;
};
为了更好地理解它的作用,让我们看一下如何将事件处理程序附加到标准.net事件:
someObject.SomeEvent += (object sender,EventArgs args) => { ... };
现在我们来看看你的非标准.net事件和UpdateEventHandler
:
public delegate void UpdateEventHandler(BaseSubscription sender, BaseEvent e);
updateSource.Update += (BaseSubscription sender, BaseEvent e) => { ... };
如果您回顾conversion
函数的签名,它将返回UpdateEventHandler
代理。这意味着我们可以使用conversion
附加到Update
事件。但在我们这样做之前,conversion
函数需要Action<UpdateEventArgs>
作为输入才能使用它。我们现在就这样做:
//EventHandler<EventArgs> similarity.
Action<UpdateEventArgs> actionUpdateEventArgs = (UpdateEventArgs e) =>
{
//This is were you put your code like in a standard.net event
//This is also probably where the Observable.FromEvent() puts
//wiring necessary to make it into an IObservable<UpdateEventArgs>
};
现在我们拥有了所需的所有部分,我们可以使用类似于事件处理程序的conversion
。
updateSource.Update += conversion(actionUpdateEventArgs);
每次actionUpdateEventArgs
被提升时,都会调用Update
内的代码。
希望这足以解释参数Func<Action<UpdateEventArgs>, UpdateEventHandler> conversion
。
最后,您将使用FromEvent()
方法:
Func<Action<UpdateEventArgs>, UpdateEventHandler> conversion
= (handlerOfUpdateEventArgs) =>
{
UpdateEventHandler handler = (BaseSubscription sender, BaseEvent e) =>
handlerOfUpdateEventArgs(sender, new UpdateEventArgs(e));
return handler;
};
var updates = Observable.FromEvent<UpdateEventHandler, UpdateEventArgs>(
// conversion (Func<EventHandler<UpdateEventArgs>, UpdateEventHandler>)
conversion,
// addHandler (Action<UpdateEventHandler>)
handler => updateSource.Update += handler,
// removeHandler (Action<UpdateEventHandler>)
handler => updateSource.Update -= handler
);
这就是我对新手观点的理解,所以我希望它会有所帮助。