Can someone explain what is happening behind the scenes? - c#

It's not entirely obvious to me what's happening in this situation.
I'd expect both functions to be fired.
Either the EventHander class is storing the list of functions to fire as an array - and the array is copied to a new one every time something is added/removed - or when the assignment is made, the whole thing is copied to a new "collection" - and not just a reference.
Somebody please enlighten me :D
Here's a little Linqpad script:
public class Moop
{
public EventHandler myEvent;
}
void Main()
{
var moo = new Moop();
moo.myEvent += (o, sender) => { "Added to Moop #1".Dump(); };
var moo2 = new Moop();
//Copy the reference, I assume?
moo2.myEvent = moo.myEvent;
moo2.myEvent += (o, sender) => { "Added to Moop #2".Dump(); };
//Fire the event on #1
moo.myEvent(null, null);
}

Event handler lists are delegates, and delegates are immutable -- like strings. So you do copy the delegate, and the second event handler gets "added to" the 2nd delegate, not the first.
You can find out more about delegates at http://www.c-sharpcorner.com/uploadfile/Ashush/delegates-in-C-Sharp/
Good luck!

Related

How to register listener to Actions stored in a Dictionary

For a game I'm developing I'm keeping track of a GameState to determine which systems should be active. To enable systems to register themselves to State changes, I've written the following code:
public static Action<State> OnDefaultStateChange;
public static Action<State> OnConstructionStateChange;
private static Dictionary<GameState, Action<State>> _stateChangeActions =
new Dictionary<GameState, Action<State>>()
{
{GameState.Default, OnDefaultStateChange},
{GameState.Construction, OnConstructionStateChange}
};
When a state is changed, it invokes the relevant action by looking up the GameState key in the _stateChangeActions dictionary.
Here's the strange behaviour that I can't understand.
If I subscribe to the action by using _stateChangeActions[key] += ListenerMethod;, it invokes correctly. But if I subscribe on the public static field, e.g OnDefaultStateChange += ListenerMethod;, and I invoke the action through the dictionary, it's as if there are no listeners.
I haven't been able to find out why this happens. Note: I'm using Unity Engine, and this issue isn't blocking me, I'm just curious.
Answer to your question
OnDefaultStateChange and _stateChangeActions have no relation to each other, other than the fact you use OnDefaultStateChange to initialize _stateChangeActions.
Your line with {GameState.Default, OnDefaultStateChange}, adds the object inside OnDefaultStateChange to the dictionary and not the reference, which means that _stateChangeActions[GameState.Default] is not the same as OnDefaultStateChange.
An example to show what is actually going on in your setup:
var state = new { LivesLeft = 2, ShirtColor = "brown" };
// Corresponds to 'OnDefaultStateChange'
Action<State> someAction = (s) =>
{
Console.WriteLine("Lives: " + s.LivesLeft);
};
// Corresponds to '_stateChangeActions'
Action<State> copyOfSomeAction = someAction;
// Subscribe to "OnDefaultStateChange"
someAction += (s) =>
{
Console.WriteLine("Shirt color: " + s.ShirtColor);
};
// 'someAction' is longer equal to 'copyOfSomeAction' since 'someAction'
// has been replaced with a new Action which produces the result from two other
// Actions.
someAction(state);
// Output:
// Lives: 2
// Shirt color: brown
copyOfSomeAction(state);
// Output:
// Lives: 2
As you can see OnDefaultStateChange and _stateChangeActions works as two independent objects, so "subscribing" to OnDefaultStateChange doesn't make that new subscriber available to _stateChangeActions.
How to solve your issue
I would suggest you make use of the event features in C#. I'm guessing a little on how you actually check the type of event to fire, but your event handling class could look something like this:
// MyEventHandlerClass.cs
public delegate void StateChangedEventHandler(object sender, State state);
public static event StateChangedEventHandler DefaultStateChanged;
public static event StateChangedEventHandler ConstructionStateChanged;
private static FireNewStateChangeEvent(State state) {
switch (state.StateChangeType)
{
case GameState.Default:
DefaultStateChanged.Invoke(this, state);
case GameState.Construction:
ConstructionStateChanged.Invoke(this, state);
}
}
To subscribe to events you simply do pretty much like you already do:
MyEventHandlerClass.DefaultStateChanged += ListenerMethod;
With this setup you can subscribe or unsubscribe (-=) to events from wherever.

Pass an event as a parameter to a method [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to pass an event to a method?
Is it possible to pass an event as a parameter to a method?
For example, the following method subscribes to the event, does work, and unsubscribes from the event:
void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
IEnumerable<TElement> elements,
??? elementEvent)
where TEventArgs: EventArgs
{
EventHandler<TEventArgs> handler = (sender, e) => { /* Handle an event */ };
foreach (var element in elements)
{
// Subscribe somehow
element.elementEvent += handler
}
// Do things
foreach (var element in elements)
{
// Unsubscribe somehow
element.elementEvent -= handler
}
}
Client code:
var elements = new [] { new Button(), new Button() };
SubscribeDoAndUnsubscribe(elements, ??? /* e => e.Click */);
If it's not possible, how do I achieve the similar logic in other ways? Shall I pass pair of delegates for subscribe/unsubscribe methods?
You have in fact discovered that events are not "first class" in C#; you cannot pass around an event as data. You can pass around a delegate to a method associated with a receiver as a first-class object by making a delegate. You can pass around a reference to any variable as a (mostly) first-class object. (I say "mostly" because references to variables cannot be stored in fields, stored in arrays, and so on; they are highly restricted compared to other kinds of data.) You can pass around a type by obtaining its Type object and passing that around.
But there is no way to directly pass around as data an event, property, indexer, constructor or destructor associated with a particular instance. The best you can do is to make a delegate (or pair of delegates) out of a lambda, as you suggest. Or, obtain the reflection object associated with the event and pass that around, along with the instance.
No, unfortunately not.
If you look at Reactive Extensions, that suffers from a similar problem. Three options they use (IIRC - it's been a while since I've looked):
Pass in the corresponding EventInfo and call it with reflection
Pass in the name of the event (and the target if necessary) and call it with reflection
Pass in delegates for subscription and unsubscription
The call in the latter case would be something like:
SubscribeAndDoUnsubscribe(elements,
handler => e.Click += handler,
handler => e.Click -= handler);
and the declaration would be:
void SubscribeDoAndUnsubscribe<TElement, TEventArgs>(
IEnumerable<TElement> elements,
Action<EventHandler<TEventArgs>> subscription,
Action<EventHandler<TEventArgs>> unsubscription)
where TEventArgs: EventArgs
You're trying to get around type safety, and you can't do so without using reflection. I'll show you an even simpler example of what you're trying to do.
void DoSomethingOnSomethingElse(T obj, Action method)
{
obj.method();
}
C# doesn't work this way. How does the compiler know that all Ts have the method method? It doesn't, and can't. Similarly, not every TElement in your code will have an event Click for example.
It sounds like you just want to set a single use event handler on a set of objects. You can do this quite easily...
EventHandler handler = null;
handler = (s,e) =>
{
DoSomething(e);
var b = (Button) s;
b.Click -= handler;
}
foreach (var button in buttons)
{
button.Click += handler;
}
This, obviously, only works with buttons, but as I write this, I see Jon Skeet has shown you a more general solution, so I'll end here.

event driven methods and execution sequence in c#.NET

Hypothetically speaking, if I had two methods (event handlers) driven by the same event, which method is executed first?
Example:
obj.SomeEvent += new SomeEventHandler(method1);
obj.SomeEvent += new SomeEventHandler(method2);
Which is called first?
Thanks!
It's up to the event publisher, but usually it would be whichever handler was added to the event first. That's the default implementation for an event which is basically implemented using a delegate. So for example:
SomeDelegate eventHandlers = null;
eventHandlers += FirstHandler;
eventHandlers += SecondHandler;
eventHandlers(...);
That will definitely call FirstHandler before SecondHandler. However, there's no guarantee that an event will be implemented just using delegates like that.
EDIT: While the event handling behaviour is up to the event publisher, the delegate combination part is well-specified in the C# language specification, section 7.8.4:
[...] Otherwise, the result of the operation is a new delegate instance that, when invoked, invokes the first operand and then invokes the second operand.
The BCL Delegate.Combine method makes a similar guarantee (emphasis mine):
(Return value) A new delegate with an invocation list that concatenates the invocation lists of a and b in that order. Returns a if b is null, returns b if a is a null reference, and returns a null reference if both a and b are null references.
The first subscribed one. "First in - first served".
The default implementation will cause event handlers to be called in the order they were added, however, it is possible to customize this behaviour. If the behaviour is customized, the client cannot tell this. So the real answer to your question is that the order in which event handlers is raised "depends" and could even change at runtime, however, the vast majority of events have default implementation.
For example:
public class ReverseBling
{
private readonly List<EventHandler> _blings = new List<EventHandler>();
public event EventHandler Bling
{
add
{
_blings.Add(value);
}
remove
{
_blings.Remove(value);
}
}
public void RaiseBling()
{
for (int i = _blings.Count - 1; i >= 0; i--)
{
_blings[i](this, EventArgs.Empty);
}
}
}
private static void Main()
{
ReverseBling bling = new ReverseBling();
bling.Bling += delegate { Console.WriteLine(0);};
bling.Bling += delegate { Console.WriteLine(1); };
bling.Bling += delegate { Console.WriteLine(2); };
bling.RaiseBling();
}
Output:
2
1
0
There is no way of telling which event handler will be invoked first. Many people think the first one to subscribe will be invoked first (which is normally the case) but not specified by the CLI.

Is there a way to accomplish the equivalent of passing an "event" by reference?

I put "event" in quotes because I realize that it's a bit of syntax sugar, rather than a true type.
I have some events which are simply chained to matching events in another class. So when the event is raised, the passage is like
Raiser -> Proxy -> Subscriber
So in the Proxy class I have a common pattern like this:
Raiser.SomeEvent +=
(_, args) =>
{
if (this.SomeEvent != null)
this.SomeEvent(this, args);
};
To tidy up my code I wanted to move this out to another method that returns a new delegate that wraps the above event-calling code:
public static EventHandler GetHandlerDelegate(EventHandler handler, Object sender)
{
return
(_, args) =>
{
if (handler != null)
handler(sender, args);
};
}
And then in Proxy I can just do:
Raiser.SomeEvent += GetHandlerDelegate(this.SomeEvent, this);
Which is much neater.
Well this is fine as long as Subscriber doesn't decide to subscribe to Proxy.SomeEvent after the above call. Unfortunately I'm not passing the "event" around by reference as I'd hoped; I now understand that I'm just passing the invocation list, so when OtherClass.SomeEvent happens and that anonymous method is called and invokes the "event" (delegate) it was given, only the delegates that had been added to that event at the time I called GetHandlerDelegate() will be called. While that would actually suffice for my current situation, it's really not acceptable to code it that way.
I've read some other SO questions and I gather there is something called Reactive Extensions that might help, but at this time I'm looking for a simpler solution if there is one. (If not, I just won't do this.)
Is there another way I can accomplish what I'm trying to do, without said drawback?
If this question is unclear, please see my answer which hopefully helps clarify it.
EDIT: Okay, I think I get the point now. It's actually quite simple. You should be able to write the proxy to just have an event, and then make the proxy itself subscribe to the Raiser's event, like this (just for EventHandler - I'll come to that later on):
Proxy proxy = new Proxy();
raiser.SomeEvent += Proxy.Handler;
// Then in the subscriber...
proxy.ProxiedEvent += (whatever)
// And the proxy class...
public class Proxy
{
public event EventHandler ProxiedEvent;
public void Handler(object sender, EventArgs e)
{
EventHandler proxied = ProxiedEvent;
if (proxied != null)
{
// Or pass on the original sender if you want to
proxied(this, e);
}
}
}
Now, the difficulty here is getting it to work generically. I can't currently think of any way of doing that, although I'm somewhat distracted right now.
Is this the sort of thing you were thinking of, or does it at least help you think about things differently?
Since my original goal of doing:
Raiser.SomeEvent += GetHandlerDelegate(this.SomeEvent, this);
is impossible, I've compromised and come up with this:
Raiser.SomeEvent += (_, args) => RaiseEvent(this.SomeEvent, this, args);
Whereas GetHandlerDelegate() would return a delegate which raises the event, RaiseEvent() simply (you guessed it) raises the event.
public static void RaiseEvent(EventHandler _event, Object sender, EventArgs args)
{
if (_event != null)
_event(sender, args);
}
And to support events using custom EventArgs:
public static void RaiseEvent<TArgs>(EventHandler<TArgs> _event, Object sender, TArgs args)
where TArgs : EventArgs
{
if (_event != null)
_event(sender, args);
}
I've put these methods in a static helper class, so the actual call is slightly uglier; here's an example:
ViewControl.OpenFilesetClick += (_, args) => EventHelper.Raise(OpenFilesetClick, this, args);
(I also renamed the method to Raise() and dropped the optional this from the event name being passed).
But I'm not entirely convinced if this is worthwhile, considering the alternative was arguably easier to read:
ViewControl.OpenFilesetClick += (_, args) =>
{
if (OpenFilesetClick != null)
OpenFilesetClick(this, args);
};
Anyway, it was an interesting way to learn more about how events and delegates work (or how they don't work).

Initializing events with initializer syntax

I often want to write something like this:
new Form
{
Text = "Caption",
Controls =
{
new Button { Text = "Button 1", Click = (s, e) => MessageBox.Show("Button 1 Clicked"), Location = new Point(10, 10) },
new Button { Text = "Button 2", Click = new EventHandler(Button2Clicked), Location = new Point(10, 40) },
new Button { Text = "Button 3", Click = Button3Clicked, Location = new Point(10, 70) },
},
}
Initializer syntax is just sugar, so why can't the compiler figure out how to generate code for an event subscription?
Gimme some sugar, baby!
When initializer syntax was invented, someone must have thought about events and rejected them. I've been trying to imagine what the rationale might have been and am coming up blank.
Is it because an event is a multi-cast object that might have more than one subscriber? No, this is an initialization process; There can be no other subscribers. [Updated] Not true, initializers are applied post-construction and an object can subscribe to its own events.
A note to Eric: I've heard the Why doesn't C# implement feature X speech. In this case, someone was already there, implementing initializers.
Updated
There seems to be contention/confusion because I used Click = in my example. The actual syntax is not relevant to the question. It could just as easily be Click += which mirrors the way you have to add a handler normally. I prefer the former because it's consistant with the rest of the initializer syntax, but ultimately I don't care, just so long as I can subscribe to an event in an initializer list.
Another Update
I do realize that adding the feature now is probably unlikely. The first issue that comes to mind is that Intellisense has to be updated. There are probably many other things that would hinder adding this feature now. My question is: Why didn't they add it in the first place. There must have been something compelling that warrented the 'nay' vote.
I cannot see any reason why they could not have provided this small teaspoon of sugar, I guess they just didn't!
There is already quite a lot of syntactic sugar involved in events, if simply declare an event on a class without providing your own implementation, the compiler is providing a delegate backing field for you, plus add / remove 'method' implementations. ALso, when you add an event handler, the compiler uses delegate inference, allowing you to simply point to a method, rather than create a delegate that represents the method.
Interestingly, Mono C# does allow you to add an event handler in an object initializer:
http://tirania.org/blog/archive/2009/Jul-27-1.html
Time to switch to Mono ;-)
Try simply assigning an event:
Click = (o,e) => { <CODE> }
Doesn't work. Initializers work only with things you can directly assign like that. This is because events need to be able to notify anyone they want (you shouldn't be allowed to remove someone else's registration for that event on accident).
I'm not sure if this is their reasoning, but it works for me.
There's a big difference between fields and events. There's an excellent article here outlining the differences, but that's the answer to your question: A field can be assigned a value; an event looks like a field but is a very different beast.
Edit
From the article I linked to:
We have seen that the event keyword is a modifier for a delegate declaration that allows it to be included in an interface, constrains its invocation from within the class that declares it, provides it with a pair of customizable accessors (add and remove), and forces the signature of the delegate
Remember that event is a shortcut; behind the scenes, the compiler creates an object with add() and remove() methods. Like:
public class Button {
public event EventHandler Click {
void add {...}
void remove {...}
}
}
Perhaps this will offer some insight... :
Button btn = new Button {Click += (s, e) => MessageBox.Show("hello")};
The error message you get is "Cannot initialize type 'Button' with a collection initializer because it does not implement IEnumerable"
Still another note... if you assign the event handler from within the form, you can do this:
this.button1.Click += (s, e) => this.textBox1.Text = e.ToString();
You couldn't access form variables from the code you've created. I get where you're coming from, and I don't disagree... what you're doing could be made to work. I guess my point is that there are reasons why the decision was made not to make it work.
Yep, should be part of the language!
But, here's a tricky workaround that lets you subscribe to events within an initializer list...
public class TestClass
{
public class MyButton : Button
{
public EventHandler ClickSubscriber
{
get { return null; }
set { Click += value; }
}
}
public static void RunTest()
{
new Form
{
Text = "Caption",
Controls =
{
new MyButton
{
ClickSubscriber = (s, e) =>
MessageBox.Show("Button 1 Clicked"),
},
},
};
}
}

Categories

Resources