6

これが私の言いたいことです。イベントを公開する API を使用しているとしますが、これらのイベントは標準EventHandlerまたはEventHandler<TEventArgs>署名に従っていません。たとえば、1 つのイベントは次のようになります。

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。基本的には、それ自体が独自のものであるからです。

明らかに、(つまり)から派生する独自のクラスを定義し、イベントを提供するオブジェクトをラップする別のクラスを作成し、ラッパー クラスに である独自のイベントを与え、そのから取得することができます。しかし、それは面倒な作業量です。EventArgsUpdateEventArgsUpdate UpdateEventHandler<UpdateEventArgs>IObservable<UpdateEventArgs>

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# を好みますが、このコードは 1 つの作業です私の同僚は主に 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 には、そのようなクラスはありません。だから、最低限、私はそれを書く必要があります。しかし、それはかなり些細なことです (基本的に 1 つのプロパティ: 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

updateSource が消えない場合は、これを怠惰な方法で行うこともできます。

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

ジョンの計画はおそらくより良いものですが、サブジェクトはあなたを助けることができます.

于 2010-06-22T05:43:01.533 に答える
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ここで行う必要があると思います)。からデリゲート型へのコンバーターを提供する必要がありEventHandler(Of BaseEvent)ます。これはおそらく、引数が渡された特定のイベント ハンドラーを呼び出すためのラムダ式です。追加アクションと削除アクションは、通常のイベント サブスクリプション コードですが、デリゲートとして表現されます。

残念ながら、私の VB は、これらすべてをきちんと表現するのに十分ではありません。または、実際、VB 9 または 10 でどれだけ簡単に利用できるかを知ることもできません。 C# の完全な例では、サブスクリプション ビットを埋める必要がありましたが、確かにそれを行うことができました...

于 2010-05-26T17:03:56.693 に答える
1

今後の参考のために、これは例として FileSystemEventHandler を使用して、FromEvent の変換オーバーロードを使用する例です。

    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

非標準の .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# の署名を 1 行ずつ分析してみましょう。

注: 標準の .net イベントと非標準のイベントを区別するために、ラムダ式にデータ型を含める場合があります。

最初の部分:

IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(

これは、関数 FromEvent がTDelegateandTEventArgs 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>(

第二部:

次に、 、 、および の 3 つの入力conversionaddhandlerありremovehandlerます。

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

私たちはそれを知ってaddHandlerおりremoveHandler、イベントからデリゲートを追加および削除するだけです。まずはこの2つをやってみましょう。

// 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>を使用する前に as 入力が必要です。今それをしましょう:

//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 に答える
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 に答える