2016-10-19 15 views
2

多くのイベントを含むサードパーティライブラリを使用しなければならない状況にあり、非常にうまく書かれていません。それは私のコードで処理する必要があるイベントを起動しますが、私はアダプタを必要とするので、私はそれを抽象化しようとしています。問題は、イベントの一部がrefのパラメータをとる代理人型であることです。ここでは、サードパーティのライブラリがどのように見えるかの例です:私は、パラメータのリストを取って定期的にイベントを抽象化していますどのように表示するにはrefパラメータを含むC#イベントの適用

delegate void AdapteeEventHandler1(SpecificAdaptee sender, int a, int b); 
delegate void AdapteeEventHandler2(SpecificAdaptee sender, ref int a); // problematic delegate 

class SpecificAdaptee 
{ 
    public event AdapteeEventHandler1 Event1; 
    public event AdapteeEventHandler2 Event2; // problematic event 

    /// <summary>Exercise Event1</summary> 
    public void FireEvent1() 
    { 
     Event1?.Invoke(this, 1, 2); 
    } 
    /// <summary>Exercise Event2</summary> 
    public void FireEvent2() 
    { 
     int a = 42; 
     Event2?.Invoke(this, ref a); 
    } 
} 

、それはタイプAdapteeEventHandler1Event1が含まれています。問題のタイプがAdapteeEventHandler2ですが、私は、全体のことを適応についてつもりですか最初にお見せしましょう:

#region AdaptedEventArgs 
class AdaptedEventArgs1 : EventArgs 
{ 
    public int A { get; set; } 
    public int B { get; set; } 
} 

class AdaptedEventArgs2 : EventArgs 
{ 
    public int A { get; set; } 
} 
#endregion 

/// <summary>These represent an abstraction layer between SpecificAdaptee and our own code</summary> 
class Adaptor 
{ 
    private readonly SpecificAdaptee _specificAdaptee; 
    /// <summary>Maintains relationship between the event triggered by SpecificAdaptee and the adapted event.</summary> 
    private readonly IAdaptedEventHandlerManager _adaptedEventHandlerManager; 

    public Adaptor(SpecificAdaptee specificAdaptee, IAdaptedEventHandlerManager adaptedEventHandlerManager) 
    { 
     _specificAdaptee = specificAdaptee; 
     _adaptedEventHandlerManager = adaptedEventHandlerManager; 
    } 

    #region Events 
    /// <summary>Adapts SpecificAdaptee.Event1</summary> 
    public event EventHandler<AdaptedEventArgs1> AdaptedEvent1 
    { 
     add 
     { 
      _specificAdaptee.Event1 += _adaptedEventHandlerManager.RegisterEventHandler<AdapteeEventHandler1>(value, 
       (sender, a, b) => value.Invoke(this, new AdaptedEventArgs1 { A = a, B = b })); 
     } 
     remove 
     { 
      _specificAdaptee.Event1 -= _adaptedEventHandlerManager.UnregisterEventHandler<AdapteeEventHandler1>(value); 
     } 
    } 

    /// <summary>Adapts SpecificAdaptee.Event2</summary> 
    public event EventHandler<AdaptedEventArgs2> AdaptedEvent2 
    { 
     add 
     { 
      /* !!! ERROR HERE !!! */ 
      _specificAdaptee.Event2 += _adaptedEventHandlerManager.RegisterEventHandler<AdapteeEventHandler2>(value, 
       (sender, a) => value.Invoke(this, new AdaptedEventArgs2 { A = a })); 
     } 
     remove 
     { 
      _specificAdaptee.Event2 -= _adaptedEventHandlerManager.UnregisterEventHandler<AdapteeEventHandler2>(value); 
     } 
    } 
    #endregion 
} 

だから何がここで起こっていることは、私がAdaptor.AdaptedEvent1にイベントハンドラを登録するとき、私はAdapteeEventHandler1EventHandler<AdaptedEventArgs1>を包んだということで、 SpecificAdaptee.Event1に登録し、またAdaptedEventArgs1AdapteeEventHandler1が必要とするパラメータのリストに変換します。このようにして、ユーザはのイベントに登録することができ、SpecificAdapteeがそれ自身のイベントを発生させると解雇されます。次に、私はこれを実行するプログラムを投稿しますが、問題はAdaptedEvent2にあることに注意してください。私は同様の方法でやりたいのですが、refパラメータを処理する方法がわかりません(構文エラーがあります。AdaptedEvent2addアクセサ ここでは、プロジェクトを行使コンソールアプリケーションです:

class Program 
{ 
    public static void Main(string[] args) 
    { 
     var specific = new SpecificAdaptee(); 
     var adapter = new Adaptor(specific, new AdaptedEventHandlerManager()); 

     adapter.AdaptedEvent1 += OnAdaptedEvent1; 
     adapter.AdaptedEvent2 += OnAdaptedEvent2; 

     specific.FireEvent1(); 
     specific.FireEvent2(); 

     Console.ReadLine(); 
    } 

    private static void OnAdaptedEvent1(object sender, AdaptedEventArgs1 args) 
    { 
     Console.WriteLine($"{nameof(OnAdaptedEvent1)}({sender}, {args.A}, {args.B})"); 
    } 

    private static void OnAdaptedEvent2(object sender, AdaptedEventArgs2 args) 
    { 
     Console.WriteLine($"{nameof(OnAdaptedEvent2)}({sender}, {args.A})"); 
    } 
} 

だから、私は自分のコードを持っている私のAdaptorのイベントに登録し、動作するようになっています方法ですと、イベントが時に解雇します。サードパーティのライブラリ(SpecificAdaptee)は、独自のイベントを発生させます(この例では、specific.FireEvent1()と2を呼び出してトリガーされます)。

あなたはそれを自分で試すことができますので、完全性のために

は、私がSpecificAdapteeのハンドラにイベントハンドラを適応マップAdaptedEventHandlerManagerするためのコードを含むので、私は通常どうなるように私には、登録し、複数のイベントハンドラの登録を解除することができます

interface IAdaptedEventHandlerManager 
{ 
    TSpecificEventHandler RegisterEventHandler<TSpecificEventHandler>(object adaptedEventHandler, 
     TSpecificEventHandler specificEventHandler); 

    TSpecificEventHandler UnregisterEventHandler<TSpecificEventHandler>(object adaptedEventHandler) 
     where TSpecificEventHandler : class; 
} 

class AdaptedEventHandlerManager : IAdaptedEventHandlerManager 
{ 
    /// <summary> 
    ///  Remembers relation between the specific handler and general handler. Important when unsubscribing from 
    ///  events. Key is the general event handler we are registering to events of this class. Value are specific 
    ///  event handlers. 
    /// </summary> 
    private readonly Dictionary<object, List<object>> _eventHandlers = 
     new Dictionary<object, List<object>>(); 

    public TSpecificEventHandler RegisterEventHandler<TSpecificEventHandler>(object adaptedEventHandler, 
     TSpecificEventHandler specificEventHandler) 
    { 
     List<object> eventHandlerList; 
     if (!_eventHandlers.TryGetValue(adaptedEventHandler, out eventHandlerList)) 
     { 
      eventHandlerList = new List<object> { specificEventHandler }; 
      _eventHandlers.Add(adaptedEventHandler, eventHandlerList); 
     } 
     else 
     { 
      eventHandlerList.Add(specificEventHandler); 
     } 
     return specificEventHandler; 
    } 

    public TSpecificEventHandler UnregisterEventHandler<TSpecificEventHandler>(object adaptedEventHandler) 
     where TSpecificEventHandler : class 
    { 
     List<object> eventHandlerList; 
     if (!_eventHandlers.TryGetValue(adaptedEventHandler, out eventHandlerList)) 
     { 
      return null; 
     } 

     var eventHandler = eventHandlerList.FirstOrDefault(); 
     if (eventHandler != null) 
     { 
      eventHandlerList.Remove(eventHandler); 
     } 

     if (!eventHandlerList.Any()) 
     { 
      _eventHandlers.Remove(adaptedEventHandler); 
     } 
     return eventHandler as TSpecificEventHandler; 
    } 
} 

これは、基本的に、適合イベントハンドラとSpecificAdapteeのハンドラのリストを辞書に記憶しています。 refパラメータを取るカスタムdelegateタイプに退避させることなく、refパラメータを取ってイベントを適応させる方法があるので、私は、カスタムEventArgs子孫で標準EventHandler<>クラスを使用することができます。

だから私の質問は

私はそれがかなりのコードであることを認識していますので、何かが明確でない場合は教えてください。前もって感謝します。

+0

デバッグヘルプ(「なぜこのコードは機能していませんか?」)には、目的の動作、特定の問題またはエラー、および質問自体に再現するのに必要な最短コードが含まれている必要があります。明確な問題文がない質問は、他の読者にとって有用ではありません。参照:最小、完全、および検証可能な例を作成する方法。あなたのコメントのために – mybirthname

+0

@mybirthnameに感謝します。これは、実際には、目的の動作(AdaptedEvent1)と問題(AdaptedEvent2)の両方を表す、最短のコードです:(ポストの最後の特定の問題を強調しようとしました。 – Caleb9

+0

あなたは 'a'を操作する必要がありますか? – toadflakz

答えて

2

refイベントのパラメータは、サブスクライバから設定されます。それは悪い考えですが、使用しているAPIはそれに基づいて動作します。

アダプタークラスのすべての苦労を取り、消費者がrefパラメーターによって汚染されないように動作させることができます。彼らはEventArgsスタイルイベントを引き続き使用できます。

public event EventHandler<AdaptedEventArgs2> AdaptedEvent2 
{ 
    add 
    { 
     _specificAdaptee.Event2 += _adaptedEventHandlerManager.RegisterEventHandler<AdapteeEventHandler2>(value, 
      (SpecificAdaptee sender, ref int a) => 
       { 
        var args = new AdaptedEventArgs2 { A = a }; 
        value.Invoke(this, args); 
        a = args.A; 
       }); 
    } 
    remove 
    { 
     _specificAdaptee.Event2 -= _adaptedEventHandlerManager.UnregisterEventHandler<AdapteeEventHandler2>(value); 
    } 
} 

イベントが実行された後、我々がrefパラメータaAの値を設定します。これは、refパラメータの動作をシミュレートし、アダプタクラスの下でそれを抽象化します。イベントハンドラでAが変更された場合は、SpecificAdapteeクラスにも反映されます。

これはREFパラメータと同様にどのように機能するかを表示するには:

class SpecificAdaptee 
{ 
    ... 
    public void FireEvent2() 
    { 
     int a = 42; 
     if (Event2 != null) 
      Event2(this, ref a); 

     Console.WriteLine("A value after the event is {0}", a); 
    } 
} 

private static void OnAdaptedEvent2(object sender, AdaptedEventArgs2 args) 
{ 
    Console.WriteLine($"{nameof(OnAdaptedEvent2)}({sender}, {args.A})"); 
    args.A = 15; 
} 

これが印刷されます。

A value after the event is 15 

PS:簡潔にするために、私は変更を必要とするプログラムの部分だけを追加しました。

+0

素晴らしいです!ありがとうございます:) – Caleb9

関連する問題