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
{
}

我认为问题是因为我每次都在“新建”代表,所以在添加/删除时它不会解析为相同的值,有没有办法投射代表?我确实有一个解决方案,但它需要为每个事件都有一个字段,因此希望有任何避免这种情况的解决方案:

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 回答