3

クラスの非ジェネリック インターフェイスとジェネリック インターフェイスがある設計パターンがあります (これが一般的に使用される DP かどうかは不明です。誰かがその名前を持っている場合はお知らせください)。実装はジェネリック値を格納し、ジェネリック インターフェイスを暗黙的に実装します。また、各プロパティが非ジェネリック形式に適切にキャストされたジェネリック プロパティの値を返す非ジェネリック インターフェイスを明示的に実装します。これはプロパティに対しては非常にうまく機能しますが、イベントに対してもうまく機能させるにはいくつか問題があります。

以下は、私がやっていることの非常に単純化されたバージョンです。Event のいずれかのインターフェイス バージョンにハンドラーを追加すると、それを同じイベントに追加して、イベントが発生したときにサブスクライブされた方法が問題にならないようにするという考え方です。Main のテスト コードは、イベント ハンドラーが期待どおりに削除されていないことを示しています。INormalInterface.Event の追加/削除ブロックを使用してイベントに追加/削除するための正しいコードは何ですか?

class Program
{
    static void Main(string[] args)
    {
        INormalInterface x = new ImplementingClass<int>();

        Console.WriteLine("Created x and invoking...");
        x.InvokeEvent();

        Console.WriteLine("Adding event and invoking...");
        x.Event += x_Event;
        x.InvokeEvent();

        Console.WriteLine("Removing event and invoking...");
        x.Event -= x_Event;
        x.InvokeEvent();

        Console.WriteLine("Done.");
        Console.ReadKey(true);
    }

    static void x_Event(object sender, NormalEventArgs e)
    {
        Console.WriteLine("Event Handled!");
    }
}

interface INormalInterface
{
    event EventHandler<NormalEventArgs> Event;

    void InvokeEvent();
}

interface IGenericInterface<T> : INormalInterface
{
    new event EventHandler<GenericEventArgs<T>> Event;
}

class ImplementingClass<T> : IGenericInterface<T>
{
    public event EventHandler<GenericEventArgs<T>> Event;
    event EventHandler<NormalEventArgs> INormalInterface.Event
    {
        add { Event += new EventHandler<GenericEventArgs<T>>(value); }
        remove { Event -= new EventHandler<GenericEventArgs<T>>(value); }
    }

    public void InvokeEvent()
    {
        if (Event != null)
        {
            Event(this, new GenericEventArgs<T>());
        }
    }
}

class NormalEventArgs : EventArgs
{
}

class GenericEventArgs<T> : NormalEventArgs
{
}

問題は、デリゲートを毎回「新しく」作成しているため、追加/削除時に同じ値に解決されないためだと思います。デリゲートをキャストする方法はありますか? 私には1つの解決策がありますが、各イベントにフィールドが必要なので、これを回避する解決策をいただければ幸いです:

class ImplementingClass<T> : IGenericInterface<T>
{
    private readonly Dictionary<EventHandler<NormalEventArgs>, EventHandler<GenericEventArgs<T>>> m_eventDictionary = new Dictionary<EventHandler<NormalEventArgs>, EventHandler<GenericEventArgs<T>>>();

    public event EventHandler<GenericEventArgs<T>> Event;
    event EventHandler<NormalEventArgs> INormalInterface.Event
    {
        add { Event += m_eventDictionary[value] = new EventHandler<GenericEventArgs<T>>(value); }
        remove { Event -= m_eventDictionary[value]; }
    }

    public void InvokeEvent()
    {
        if (Event != null)
        {
            Event(this, new GenericEventArgs<T>());
        }
    }
}
4

3 に答える 3

3

これでうまくいきますが、私はそれをきれいとは呼びません。

    event EventHandler<NormalEventArgs> INormalInterface.Event
    {
        add
        {
            var handler = (EventHandler<GenericEventArgs<T>>)Delegate.CreateDelegate(typeof(EventHandler<GenericEventArgs<T>>), value.Target, value.Method);
            Event += handler;
        }
        remove
        {
            var handler = (EventHandler<GenericEventArgs<T>>)Delegate.CreateDelegate(typeof(EventHandler<GenericEventArgs<T>>), value.Target, value.Method);
            Event -= handler;
        }
    }

の問題

    add { Event += new EventHandler<GenericEventArgs<T>>(value); }

これは、Delegate.Invokeメソッドのデリゲートを作成するため、イベントのマルチキャストデリゲートで一致するものを見つけることができないということです。これは、新しいオブジェクト自体の作成ではなく、ハンドラーの削除を妨げるものです。

于 2012-11-09T12:18:18.753 に答える
1

新しい答え

最もきれいではありませんが、これはうまくいくようです:

    event EventHandler<NormalEventArgs> INormalInterface.Event
    {
        add { Event += new EventHandler<GenericEventArgs<T>>(value); }
        remove
        {
            var d = Event.GetInvocationList().First(x => x.Target.GetHashCode() == value.GetHashCode());
            Event -= (EventHandler<GenericEventArgs<T>>) d;
        }
    }

元の回答:


インターフェースが間違った方法で使用されているように思えます-既存の理由がない限り、次のように変更します。

class Program
{
    static void Main(string[] args)
    {
        IGenericInterface<int> x = new ImplementingClass<int>();

        Console.WriteLine("Created x and invoking...");
        x.InvokeEvent();

        Console.WriteLine("Adding event and invoking...");
        x.Event += x_Event;
        x.InvokeEvent();

        Console.WriteLine("Removing event and invoking...");
        x.Event -= x_Event;
        x.InvokeEvent();

        Console.WriteLine("Done.");
        Console.ReadKey(true);
    }

    static void x_Event(object sender, NormalEventArgs e)
    {
        Console.WriteLine("Event Handled!");
    }
}

interface IBaseInterface<T> where T : EventArgs
{
    event EventHandler<T> Event;

    void InvokeEvent();
}

interface INormalInterface : IBaseInterface<NormalEventArgs>
{
}

interface IGenericInterface<T> : IBaseInterface<GenericEventArgs<T>>
{
}

class ImplementingClass<T> : IGenericInterface<T>
{
    public event EventHandler<GenericEventArgs<T>> Event;

    public void InvokeEvent()
    {
        if (Event != null)
        {
            Event(this, new GenericEventArgs<T>());
        }
    }
}

class NormalEventArgs : EventArgs
{
}

class GenericEventArgs<T> : NormalEventArgs
{
}
于 2012-11-09T11:54:40.580 に答える
0

ここでオブザーバーパターンを使用できると思いますhttp://www.dofactory.com/Patterns/PatternObserver.aspx

using System;
using System.Collections.Generic;

namespace DoFactory.GangOfFour.Observer.RealWorld
{
  /// <summary>
  /// MainApp startup class for Real-World
  /// Observer Design Pattern.
  /// </summary>
  class MainApp
  {
    /// <summary>
    /// Entry point into console application.
    /// </summary>
    static void Main()
    {
      // Create IBM stock and attach investors
      IBM ibm = new IBM("IBM", 120.00);
      ibm.Attach(new Investor("Sorros"));
      ibm.Attach(new Investor("Berkshire"));

      // Fluctuating prices will notify investors
      ibm.Price = 120.10;
      ibm.Price = 121.00;
      ibm.Price = 120.50;
      ibm.Price = 120.75;

      // Wait for user
      Console.ReadKey();
    }
  }

  /// <summary>
  /// The 'Subject' abstract class
  /// </summary>
  abstract class Stock
  {
    private string _symbol;
    private double _price;
    private List<IInvestor> _investors = new List<IInvestor>();

    // Constructor
    public Stock(string symbol, double price)
    {
      this._symbol = symbol;
      this._price = price;
    }

    public void Attach(IInvestor investor)
    {
      _investors.Add(investor);
    }

    public void Detach(IInvestor investor)
    {
      _investors.Remove(investor);
    }

    public void Notify()
    {
      foreach (IInvestor investor in _investors)
      {
        investor.Update(this);
      }

      Console.WriteLine("");
    }

    // Gets or sets the price
    public double Price
    {
      get { return _price; }
      set
      {
        if (_price != value)
        {
          _price = value;
          Notify();
        }
      }
    }

    // Gets the symbol
    public string Symbol
    {
      get { return _symbol; }
    }
  }

  /// <summary>
  /// The 'ConcreteSubject' class
  /// </summary>
  class IBM : Stock
  {
    // Constructor
    public IBM(string symbol, double price)
      : base(symbol, price)
    {
    }
  }

  /// <summary>
  /// The 'Observer' interface
  /// </summary>
  interface IInvestor
  {
    void Update(Stock stock);
  }

  /// <summary>
  /// The 'ConcreteObserver' class
  /// </summary>
  class Investor : IInvestor
  {
    private string _name;
    private Stock _stock;

    // Constructor
    public Investor(string name)
    {
      this._name = name;
    }

    public void Update(Stock stock)
    {
      Console.WriteLine("Notified {0} of {1}'s " +
        "change to {2:C}", _name, stock.Symbol, stock.Price);
    }

    // Gets or sets the stock
    public Stock Stock
    {
      get { return _stock; }
      set { _stock = value; }
    }
  }
}
于 2012-11-09T11:30:56.057 に答える