我的代码遇到了一些设计问题.
我有一个创建子对象的对象(该子对象然后可以创建另一个子对象,依此类推),并且两个对象都订阅同一事件.
但是,我只希望最多子对象接收事件.
我的项目概述:
我正在创建一个IVR系统.当用户调用系统时,该用户将具有X菜单选项.根据用户的选择,他们将有一个选择子菜单,依此类推.我为此使用状态机.用户在手机上按数字时,每个状态机都需要“监听”.但是只有当前的状态机需要处理输入的号码.每个状态机都可以创建一个新的状态机来表示子菜单.
这是一些示例代码:
基类:
public delegate void DoSomething(object sender, EventArgs data);
public class Base
{
public event DoSomething myEvent;
private IObject foo;
public Base ()
{
foo = new myObjectA(this);
}
public void SomeAction()
{
((myObjectA)foo).CreateChild();
}
public void EventFired()
{
if (myEvent != null)
{
myEvent(this, new EventArgs());
}
}
}
对象A:
class myObjectA : IObject
{
private Base theCallingObject;
private IObject child;
public myObjectA (Base _base)
{
theCallingObject = _base;
theCallingObject.myEvent += new DoSomething(theCallingObject_myEvent);
}
public void CreateChild()
{
child = new myObjectB(theCallingObject);
}
void theCallingObject_myEvent(object sender, EventArgs data)
{
// Handle event
MessageBox.Show("myObjectA");
}
}
对象B:
class myObjectB : IObject
{
private Base theCallingObject;
public myObjectB (Base _base)
{
theCallingObject = _base;
theCallingObject.myEvent += new DoSomething(theCallingObject_myEvent);
}
void theCallingObject_myEvent(object sender, EventArgs data)
{
// Handle event
MessageBox.Show("myObjectB");
}
}
现在,当我这样做时:
Base blah = new Base();
blah.SomeAction();
blah.EventFired();
我同时收到A和B的消息框.
我需要实现Base,以便只有myObjectB才能获取事件.
我将拥有数百个myObject,因此我需要在Base级别而不是myObject级别的实现.另外,如果存在数百个对象,则在myObject级别进行处理仍然需要触发该事件,从而导致性能问题.
我考虑过的一种解决方案是,当myObjectA创建子项时,取消订阅该事件,然后在回到myObjectA级别时重新订阅.但是我觉得可以做得更好.
有人有想法么?
编辑:使用Payo的输入,我想出了这个:
public delegate void DoSomething(object sender, EventArgs data);
public class Base
{
private IObject foo;
private List<DoSomething> _myEventStorage;
public event DoSomething myEvent
{
add
{
_myEventStorage.Insert(0, value);
}
remove
{
_myEventStorage.Remove(value);
}
}
public Base ()
{
_myEventStorage = new List<DoSomething>();
foo = new myObjectA(this);
}
public void SomeAction()
{
((myObjectA)foo).CreateChild();
}
public void EventFired()
{
_myEventStorage[0].Invoke(this, new EventArgs());
}
}
解决方法:
对于事件,每个订户都要排队(放在列表末尾),这是一个FIFO模型.您希望最孩子的对象“拥有”该事件,而不仅仅是订阅并成为其他未知对象的某些抽象列表的一部分.
我将提供一个新的模型,该模型代表您正在尝试做的事情.这可能就是杰森(Jason)的建议:(他在我输入时回答了他的问题)
public class Base
{
private DoSomething _myEventStorage;
public event DoSomething myEvent
{
add
{
_myEventStorage = value;
}
remove
{
_myEventStorage -= value;
}
}
...
public void EventFired()
{
if (_myEventStorage != null)
{
_myEventStorage(this, new ChainEventArgs());
}
}
}
这仅在最后一次调用.另一个选项(添加到此自定义添加/删除)将是提供派生的EventArgs:
public class ChainEventArgs : EventArgs
{
public bool Handled { get; set; }
}
public delegate void DoSomething(object sender, ChainEventArgs data);
...
public event DoSomething myEvent
{
add
{
var temp = _myEventStorage;
_myEventStorage = null;
_myEventStorage += value;
_myEventStorage += temp; // now all are called, but FILO
}
remove
{
_myEventStorage -= value;
}
}
此时,您可以在每个IObject上选中Handled
void theCallingObject_myEvent(object sender, ChainEventArgs data)
{
if (data.Handled)
return;
if (I_want_to_block_parents)
data.Handled = true;
// else leave it false
}
或者,为您的基类添加一些复杂性,然后停止调用链(让子级无需检查Handled).我将使用List<>展示解决方案代表,但某些MulticaseDelegate强制类型转换和调用可以做到相同.我只是觉得List<>代码可能更具可读性/可维护性.
public class Base
{
private List<DoSomething> _myEventStorage;
public event DoSomething myEvent
{
add
{
_myEventStorage.Insert(0, value);
}
remove
{
_myEventStorage.Remove(value);
}
}
...
public void EventFired()
{
var args = new ChainEventArgs();
foreach (var handler in _myEventStorage)
{
handler(this, args);
if (args.Handled)
break;
}
}
}