C#: Hook up all events from object in single statement
In my domain layer all domain objects emit events (of type InvalidDomainObjectEventHandler) to indicate invalid state when 开发者_开发知识库the IsValid property is called.
On an aspx codebehind, I have to manually wire up the events for the domain object like this:
_purchaseOrder.AmountIsNull += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
_purchaseOrder.NoReason += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
_purchaseOrder.NoSupplier += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
_purchaseOrder.BothNewAndExistingSupplier += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
Note that the same method is called in each case since the InvalidDomainobjectEventArgs class contains the message to display.
Is there any way I can write a single statement to wire up all events of type InvalidDomainObjectEventHandler in one go?
Thanks
David
I don't think you can do this in a single statement.. But you can make the code more readible like this:
_purchaseOrder.AmountIsNull += HandleDomainObjectEvent;
_purchaseOrder.NoReason += HandleDomainObjectEvent;
_purchaseOrder.NoSupplier += HandleDomainObjectEvent;
_purchaseOrder.BothNewAndExistingSupplier += HandleDomainObjectEvent;
Other than that - seems like the answer's no :(
You can create an aggregate event in some base class (or in some helper class, or in the PurchaseOrder
class itself, if you have access to it):
abstract class BaseOrderPage : Page {
PurchaseOrder _purchaseOrder = new PurchaseOrder();
...
public event InvalidDomainObjectEventHandler InvalidDomainObjectEvent {
add {
_purchaseOrder.AmountIsNull += value;
_purchaseOrder.NoReason += value;
_purchaseOrder.NoSupplier += value;
_purchaseOrder.BothNewAndExistingSupplier += value;
}
remove {
_purchaseOrder.AmountIsNull -= value;
_purchaseOrder.NoReason -= value;
_purchaseOrder.NoSupplier -= value;
_purchaseOrder.BothNewAndExistingSupplier -= value;
}
}
}
And then just use it in the derived classes:
InvalidDomainObjectEvent += new DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent);
C# 2.0 and above:
InvalidDomainObjectEvent += HandleDomainObjectEvent;
I've used this technique successfully to aggregate events of the FileSystemWatcher
class.
You can use reflection to do this automatically. I think you want something like this:
public static void WireEvents(object subject)
{
Type type = subject.GetType();
var events = type.GetEvents()
.Where(item => item.EventHandlerType == typeof(InvalidDomainObjectEventHandler));
foreach (EventInfo info in events)
info.AddEventHandler(subject, new InvalidDomainObjectEventHandler(HandleDomainObjectEvent));
}
Then, all you have to do when you create a new object is this:
PurchaseOrder _purchaseOrder = new PurchaseOrder();
HelperClass.WireEvents(_purchaseOrder);
Don't forget that there is a performance penalty with reflection that will be apparent if you create PurchaseOrder
s and other similar objects in any great numbers.
Edit - other notes: you will need a using System.Reflection
directive. As it stands, this code needs C#3 for the var
keyword and .net framework 3.5 for the Where()
method (and - if it's not automatically generated - using System.Linq;
).
As David has done in a later answer, it can be re-written without changing the basic functionality for earlier versions.
I looked at Bob Sammers' suggestion. The compiler wasn't liking the .Where method of the EventInfo[] returned by GetEvents(), but I've changed the code slightly to the following:
private void HookUpEvents()
{
Type purchaseOrderType = typeof (PurchaseOrder);
var events = purchaseOrderType.GetEvents();
foreach (EventInfo info in events)
{
if (info.EventHandlerType == typeof(Kctc.Data.Domain.DomainObject.InvalidDomainObjectEventHandler))
{
info.AddEventHandler(_purchaseOrder, new Kctc.Data.Domain.DomainObject.InvalidDomainObjectEventHandler(HandleDomainObjectEvent));
}
}
}
After I added this method to the page, it all worked absolutely hunky dory. And I can add events to the purchase order object without having to remember to hook them up individually, which is exactly what I wanted.
You could consider to put the event handlers into an interface. Then you attach the interface:
public interface IPurchaseOrderObserver
{
void AmountIsNullEventHandler(WhateverArgs);
void NoReasonEventHandler(WhateverArgs);
void NoSupplierEventHandler(WhateverArgs);
void BothNewAndExistingSupplierEventHandler(WhateverArgs);
}
_purchaseOrder.RegisterObserver(DomainObject);
You either put this four lines into the RegisterObeserver method, or you replace the events and directly call the interfaces.
精彩评论