我有一个设计模式(不确定这是否是一个常用的DP,如果有人有它的名字,请告诉我),我有一个类的非泛型和通用接口。实现存储通用值并隐式实现泛型接口。它还明确地实现了非泛型接口,每个属性返回适当强制转换为非泛型形式的泛型属性的值。这对于属性来说非常有效,但是我遇到了一些问题,让它对事件起作用非常好。
以下是我正在做的大大简化的版本。这个想法是将处理程序添加到事件的接口版本应该将它添加到同一事件中,以便在事件触发时它与订阅的方式无关。 Main中的测试代码显示事件处理程序没有像我期望的那样被删除。使用INormalInterface.Event的添加/删除块添加/删除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>());
}
}
}
答案 0 :(得分: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方法创建委托,因此它无法在事件的多播委托中找到匹配项。是,而不是创建新对象本身,阻止您删除处理程序。
答案 1 :(得分: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
{
}
答案 2 :(得分: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; }
}
}
}