8

Rx チームの Bart De Smet による最新のビデオ: Rx Update - .NET 4.5 , Async, WinRT私は、WinRT イベントがいくつかの非常に奇妙なメタデータによって .NET に公開されているのを見ました。add_remove_

EventRegistrationToken add_MyEvent(EventHandler<MyEventArgs> handler) { … }
void remove_MyEvent(EventRegistrationToken registrationToken) { … }

登録トークンを「破棄」することでイベントからのサブスクライブを解除できます (Rx は同じ種類のことを行い、メソッドIDisposableからインスタンスを返しますSubscribe())。ということで簡単にランバ式をイベントからサブスクライブ解除できるようになったのですが…

では、C# ではこの種のイベントをどのように操作できるのでしょうか? .NET では、デリゲートの 1 つのインスタンスでメソッド (静的およびインスタンス) をサブスクライブし、同じメソッドを指す完全に別のデリゲート インスタンスでサブスクライブを解除することができます。したがって、WinRT イベントを使用して、C# で一部のデリゲート型インスタンスのサブスクライブを解除した場合...コンパイラはどこで正しいものを取得したのEventRegistrationTokenでしょうか? この魔法はどのように機能しますか?

- アップデート -

実際EventRegistrationTokenには、ある種のDispose()メソッドを呼び出すだけで購読を解除することはできません。それは本当に悲しいことです:

public struct EventRegistrationToken
{
    internal ulong Value { get; }
    internal EventRegistrationToken(ulong value)
    public static bool operator ==(EventRegistrationToken left, EventRegistrationToken right)
    public static bool operator !=(EventRegistrationToken left, EventRegistrationToken right)
    public override bool Equals(object obj)
    public override int GetHashCode()
}

-- update2 --

WinRT の相互運用性では、マネージド オブジェクトで WinRT イベントをサブスクライブするときに、登録トークンのグローバルテーブルを実際に使用します。たとえば、ハンドラーを削除するための相互運用コードは次のようになります。

internal static void RemoveEventHandler<T>(Action<EventRegistrationToken> removeMethod, T handler)
{
  object target = removeMethod.Target;
  var eventRegistrationTokenTable = WindowsRuntimeMarshal.ManagedEventRegistrationImpl.GetEventRegistrationTokenTable(target, removeMethod);
  EventRegistrationToken obj2;
  lock (eventRegistrationTokenTable)
  {
    List<EventRegistrationToken> list;
    if (!eventRegistrationTokenTable.TryGetValue(handler, out list)) return;
    if (list == null || list.Count == 0) return;
    int index = list.Count - 1;
    obj2 = list[index];
    list.RemoveAt(index);
  }
  removeMethod(obj2);
}

それは本当に悲しいことです。

4

2 に答える 2

14

次のように、WinRTイベントにデリゲートを追加または削除する場合:

this.Loaded += MainPage_Loaded;

this.Loaded -= MainPage_Loaded;

通常の.Netイベントで作業していたように見えます。しかし、このコードは実際には次のようにコンパイルされます(ReflectorはWinRTコードの逆コンパイルに問題があるようですが、これが実際のコードの機能だと思います)。

WindowsRuntimeMarshal.AddEventHandler<RoutedEventHandler>(
    new Func<RoutedEventHandler, EventRegistrationToken>(this.add_Loaded),
    new Action<EventRegistrationToken>(remove_Loaded),
    new RoutedEventHandler(this.MainPage_Loaded));

WindowsRuntimeMarshal.RemoveEventHandler<RoutedEventHandler>(
    new Action<EventRegistrationToken>(this.remove_Loaded),
    new RoutedEventHandler(this.MainPage_Loaded));

C#からadd_とメソッドにアクセスできないため、このコードは実際にはコンパイルされません。remove_しかし、ILでそれを行うことができ、それはまさにコンパイラーが行うことです。

WindosRuntimeMarshalそれらすべてを保持しEventRegistrationToken、必要に応じてそれらを使用して購読を解除するようです。

于 2011-10-14T23:52:29.093 に答える
9

「C# 言語プロジェクションに取り組んでいる驚くほど賢い人々がいる」という回答を受け入れますか?

さらに深刻なことに、イベント パターンの低レベル ABI (バイナリ) 実装が発見されました。C# 言語プロジェクションはこのパターンを認識しており、C# イベントとして公開する方法を認識しています。このマッピングを実装する CLR 内のクラスがあります。

于 2011-10-15T06:16:09.683 に答える