6

这就是我的意思。假设我正在使用一个公开事件的 API,但这些事件不遵循标准EventHandlerEventHandler<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(ie, UpdateEventArgs) 的类,编写另一个类来包装提供Update事件的对象,为包装类提供它自己的 Update事件,an EventHandler<UpdateEventArgs>,并从中IObservable<UpdateEventArgs>获取an。但这是令人讨厌的工作量。

有什么方法可以IObservable<[something]>从这样的“非标准”事件中创建一个,还是我不走运?


更新:从乔恩斯基特的回答中,我被推向以下重载的方向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,如果不编写自己的方法就真的无法完成上述任务吗?

我几乎觉得我一开始就从完全错误的角度来处理这个问题。但我真的不确定。

4

6 回答 6

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
);
于 2010-06-08T12:28:12.973 回答
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 来解密它,但是我确定某处有一个委托类型)。TEventArgs将是事件参数的一种类型(BaseEvent我认为应该在这里做)。您需要提供一个从 anEventHandler(Of BaseEvent)到您的委托类型的转换器 - 这可能只是一个 lambda 表达式,用于调用给定的事件处理程序,并将参数传递给它。添加和删​​除操作将是正常的事件订阅代码 - 但表示为委托。

不幸的是,我的 VB 不够好,无法巧妙地表达所有这些 - 或者确实不知道在 VB 9 或 10 中可以轻松获得多少。我知道它在 C# 中的外观......如果你能给我一个简短但C# 中的完整示例只让我填写订阅位,我当然可以这样做......

于 2010-05-26T17:03:56.693 回答
1

如果 updateSource 永远不会消失,您也可以以懒惰的方式执行此操作:

var observable = new Subject<BaseEvent>();
updateSource.Update += (o,e) => observable.OnNext(e);

虽然 Jon 的计划可能更好,但 Subjects 可以帮助您。

于 2010-06-22T05:43:01.533 回答
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))
于 2010-08-08T22:33:19.437 回答
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 参数。

于 2011-03-04T02:43:29.183 回答
0

我有一个类似的噩梦,因为我正在使用一个使用非标准 .net 事件的互操作 API。我是很多东西的初学者,包括泛型、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 接受 aTDelegateTEventArgs As EventArgs作为 INPUT。请注意, OUTPUTIObservable也将具有类型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>(

第二部分:

接下来,我们有三个输入conversionaddhandlerremovehandler

Func<Action<TEventArgs>, TDelegate> conversion, 
Action<TDelegate> addHandler, 
Action<TDelegate> removeHandler);

我们知道这一点addHandlerremoveHandler只需在事件中添加和删除委托。让我们先做这两个。

// 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
);

这就是我从新手的角度理解它的方式,所以我希望它会有所帮助。

于 2013-08-17T10:40:51.700 回答