I have a class with multiple EventHandlers (among other things):
public GameObject
{
public event EventHandler<EventArgs> Initialize;
public event EventHandler<EventArgs> BeginStep;
....
}
I want to be able to add a Clone() function to GameObject, which returns an exact duplicate of the object it was called on. I tried doing it like this:
public GameObject Clone()
{
var clone = new GameObject()
{
Initialize = this.Initialize,
BeginStep = this.BeginStep,
};
}
But, it appears that it is making clone.BeginStep point to the same object as this.BeginStep instead of making a copy. So, how do I make a copy of an EventHandler object?
You don't need to worry about that. The EventHandler<EventArgs> object is immutable so any change in the list of listeners in either object will cause that object to get a new EventHandler<EventArgs> instance containing the updated invocation list. This change will not be present in the other GameObject.
Try adding it with the += operator. I didn't even know it was possible to assign an event.
clone.Initialize += this.Initialize;
Also, all delegates are immutable value types, therefore you don't have to worry about them pointing to the same object - when you an operation like above, the whole delegate is copied (cloned, if you will).
It depends on whether your events are delegating to methods defined in the GameObject class or whether they delegate to to some other observer class instance.
If the events are handled in methods defined in your GameObject class and you want events in the clone to be handled by methods in your clone instance, you can get use reflection to get the method info from the original event handlers, create a new delegate using the cloned instance and the method name, and then assign the new delegate as the cloned event handler.
public GameObject Clone()
{
var clone = new GameObject();
foreach (var target in this.Initialize.GetInvocationList())
{
var mi = target.Method;
var del = Delegate.CreateDelegate(
typeof(EventHandler<EventArgs>), clone, mi.Name);
clone.Initialize += (EventHandler<EventArgs>)del;
}
return clone;
}
If the events are handled in a different class, then you don't need to do anything, but all event notifications for both the original instance and cloned instance willhave the same handlers. If that's not what you want then you'll need to change the event delegates after you clone.
You don't need to clone the events, just like you don't need to clone any methods of the source object. When you clone, all you really need to duplicate are the member/property values.
You'll want to do something akin to what was posted on Deep cloning objects
public static GameObject Clone(GameObject source)
{
// Don't serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(GameObject);
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (GameObject)formatter.Deserialize(stream);
}
}
Your class will need to be serializable.
EDIT: As I said, it was based off of the code I linked to, and I hurried to give the answer. Should've checked it a little closer.
Related
I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.
You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
Then when you hook up the event, do this:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.
Edit to answer question in the comments:
Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.
Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.
My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.
Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.
You can do it with reflection ...
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.
I am attempting to subscribe two events to an object. But the object is not instantiated before I try to add the events. Is there a way I can subscribe these two events and instantiate afterwards? I already have the delegates, event, event args and event handler working.
Sample Code:
Ares a;
public B()
{
a.up += new upEventHandler(doUp);
a.down += new downEventHandler(doDown);
a = new Ares();
}
I am attempting to subscribe two events to an object. But the object is not instantiated before I try to add the events. Is there a way I can subscribe these two events and instantiate afterwards?
No, absolutely not. It's exactly like trying to set properties on an object before the object exists. Try to think about how that would work - and then realize that subscribed event handlers are part of the state of an object just like properties are.
Obviously you could store the event handlers somewhere else and subscribe them later on, but as stated, the answer is simply no. It doesn't make any sense at a conceptual level, or a practical one.
It's not possible. You must instantiate the object first.
The closest thing you could do to what you're describing would be to make the events static.
class Ares {
public static event upEventHandler up;
public static event downEventHandler down;
// ...
}
And then modify B() to be:
public B() {
Ares.up += new upEventHandler(doUp);
Ares.down += new downEventHandler(doDown);
a = new Ares();
}
I assume that the events are fired in the constructor and you want to capture that.
Try refactoring the event firing code out of the constructor into a separate Initialize() method, so you would then have the following:
Ares a;
public B()
{
a = new Ares();
a.up += new upEventHandler(doUp);
a.down += new downEventHandler(doDown);
a.Initialize(); //do all init of the ares object here, not in constructor
}
As my code suggests, I'm trying to create a delegate which will point to the StringBuff method BuffString, which creates a StringBuilder that is going to have a fair amount of settings, etc.
My problem is that, for some reason, no matter what it is I try I can't pass the reference to the StringBuff class I made within my Sprite class to the delegate's constructor without receiving an error. Ontop of that, I feel like creating an event may be useful to help initiate the delegate.
The main problem is that I'm just now barely grasping these two concepts, as well as how to use them as replacements for function pointers which are allowed in other programming languages.
If anyone has any idea on what it is I need to do to make this work, I would definitely appreciate it.
Here's the code:
public class StringBuff
{
private static StringBuilder stringBuffer = new StringBuilder();
public static StringBuilder BuffString(string _string) //--may possibly have to use IntPtr to reference stringBuffer here.
//This is the equivalent to the "strbuff_new" C++ method variant, designed to update the stringBuffer.
{
int iCounter = 0;
stringBuffer.Append(_string + " ");
iCounter += _string.Length + 1;
if (iCounter == stringBuffer.Capacity - 1)
{
stringBuffer.Capacity += stringBuffer.Capacity;
}
return stringBuffer;
}
}
public delegate void UpdateStringBuffer(StringBuff sender);
public class Sprite : SpriteInterface.ISprite
{
private StringBuff stringBuff = new StringBuff();
public event UpdateStringBuffer stringBuffEvent
{
add
{
Console.WriteLine("Adding");
stringBuffEvent += value;
}
remove
{
Console.WriteLine("Removing...");
stringBuffEvent -= value;
}
}
static void Main()
{
new Sprite().stringBuffEvent += new UpdateStringBuffer(stringBuff);
}
}
I believe you are in need for some reading. Refer to the following:
Events Tutorial
Introduction to Delegates and Events
Events and Delegates simplified
You are misunderstanding the use of events and delegate.
When you want to add an Event Handler to an event, you pass a delegate of the same type as the event (which you did correctly)
But when you create a delegate, what you should pass in the constructor (most of the time) is a Method Name and not some variable, since a delegate is a kind of pointer to a (list of) functions.
I reccomend you to read more about delegates as Akram Shahda suggested but just for now i'll tell you that the method that you should pass as parameter to the delegate constructor should have the same signature - means return the same value and accept the same parameters. so for example you could have:
// This method have the same signature as UpdateStringBufferDelegate
public void SomeMethod (StringBuff buff)
{
// Doing somthing here
}
And then you can do in your main:
// Passing method's name and not a variable!!
new Sprite().stringBuffEvent += new UpdateStringBuffer(SomeMethod);
The Actuall parameters that will be passed to the function itself (some StringBuff) only determined at the time of the invokation of the event.
You should read more about that.
Good Luck!
you are doing it wrong,
new Sprite().stringBuffEvent += new UpdateStringBuffer(stringBuff);
Above code is invalid due to following reasons.
1. stringBuff that your UpdateStringBuffer is taking is an instance of StringBuff within Sprite.
2. You are accessing stringBuff from the static Main method which does not have any idea about stringBuff where it is located.
1- The delegate's constructor can only have a parameter Method. Ex
public delegate void UpdateStringBuffer(StringBuff sender);
2- You can declare ur event and add a method to define ur method in ur Splite class. Ex:
public event UpdateStringBuffer stringBuffEvent;
public ProcessUpdateStringBuffer(UpdateStringBuffer yourMethod)
{
stringBuffEvent += yourMethod
}
3- and from ur main u can define ur method to the event and invoke it like this:
Sprite sprite = new Sprite();
sprite.ProcessUpdateStringBuffer(UpdateStringBuffer(urMethod));
sprite.stringBuffEvent(ur parameters);
I need to find out how to perform some action (flush cache) when an object of type X is updated.
So when I save object of type Y, nothing is done, when I save unchanged object of type X nothing should happen, but when this object is changed and UPDATE is made, I want to know it.
I tried various NHibernate events (IPostUpdateEventListener, IFlushEntityEventListener, etc.) but did not succeed.
You want an IPostUpdateEventListener.
I was experiencing problem in implemented method, because in some cases I had to call the same method on default implementation, otherwise the code path ended in my code.
private readonly DefaultFlushEntityEventListener _impl = new DefaultFlushEntityEventListener();
public void OnFlushEntity(FlushEntityEvent flushEntityEvent)
{
... my code goeas here ...
_impl.OnFlushEntity(flushEntityEvent);
}
In OnFlush method of IFlushEntityEventListener I cannot detect dirty properties... etc.
But what really works is (thanks Andrew) is this code
public void OnPostUpdate(PostUpdateEvent postUpdateEvent)
{
var dirtyProperties = postUpdateEvent.Persister.FindDirty(postUpdateEvent.State, postUpdateEvent.OldState, postUpdateEvent.Entity, postUpdateEvent.Session);
int dirty = dirtyProperties.Length;
if (dirty == 0) // I want detect only modififed entities
return;
Trace.WriteLine(string.Format("OnPostUpdate({0}, {3}) in session#{1} - dirty props. {2}", postUpdateEvent.Entity.GetType().Name, postUpdateEvent.Session.GetHashCode(), dirty, postUpdateEvent.Entity.GetHashCode()));
lock (_objects)
{
if (!_objects.Contains(postUpdateEvent.Entity)) // I will manipulate this list in `AbstractFlushingEventListener.PostFlush` method
_objects.Add(postUpdateEvent.Entity);
}
}
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 PurchaseOrders 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.