I'm trying to find a way to add delegates with parameter values but without lambda expressions. I can't find a architecture which allows it - fitting to my needs. Look at following example:
public class MyDelegateClass
{
public class ArgsHolder : EventArgs
{
public object[] data;
public ArgsHolder(params object[] data)
{
this.data = data;
}
}
public event EventHandler handler;
public void Execute()
{
ArgsHolder holder = new ArgsHolder(5, 3); // this is the problem
handler?.Invoke(this, holder);
}
}
public class OutsideClass
{
public void Start()
{
MyDelegateClass del = new MyDelegateClass();
del.handler += new EventHandler(EventMethod);
del.Execute();
del.handler -= new EventHandler(EventMethod);
}
private void EventMethod(object sender, EventArgs args)
{
ArgsHolder holder = (ArgsHolder)args;
print(args.data[0] + " " + args.data[1]
}
}
The problem is, that the MyDelegateClass sets the values, which would be avoidable with a lambda expression like this:
public class OutsideClass
{
public void Start()
{
MyDelegateClass del = new MyDelegateClass();
ArgsHolder holder = new ArgsHolder(5,3);
del.handler += (sender,args) => EventMethod(this, holder);
del.Execute();
del.handler -= (sender,args) => EventMethod(this, holder); // this is now the problem
}
private void EventMethod(object sender, EventArgs args)
{
ArgsHolder holder = (ArgsHolder)args;
print(args.data[0] + " " + args.data[1]
}
}
This would be working, but now I can't remove the event which is necessary, because the subscriber objects are dynamic in my project. I hope I was precise enough in elaborating the problem at hand :)
Removal of subscribing handlers is based on reference comparison, so as long as you create one delegate from a lambda expression and store it, you can remove it:
public void Start()
{
MyDelegateClass del = new MyDelegateClass();
ArgsHolder holder = new ArgsHolder(5,3);
EventHandler eventHandler = (sender,args) => EventMethod(this, holder);
del.handler += eventHandler;
del.Execute();
del.handler -= eventHandler;
}
In a more complicated scenario where the subscribe action happens in a different place than unsubscribe you'd store the reference to the handler in a field.
Related
Let's say I want to pass some extra data when assigning an event handler. Consider the following code:
private void setup(string someData)
{
Object.assignHandler(evHandler);
}
public void evHandler(Object sender)
{
// need someData here!!!
}
How would I go about getting someData into my evHandler method?
private void setup(string someData)
{
Object.assignHandler((sender) => evHandler(sender,someData));
}
public void evHandler(Object sender, string someData)
{
// need someData here!!!
}
I had a hard time figuring out #spender's example above especially with: Object.assignHandler((sender) => evHandler(sender,someData)); because there's no such thing as Object.assignHandler in the literal sense. So I did a little more Googling and found this example. The answer by Peter Duniho was the one that clicked in my head (this is not my work):
snip
The usual approach is to use an anonymous method with an event handler
that has your modified signature. For example:
void Onbutton_click(object sender, EventArgs e, int i) { ... }
button.Click += delegate(object sender, EventArgs e)
{ Onbutton_click(sender, e, 172); };
Of course, you don't have to pass in 172, or even make the third parameter
an int. :)
/snip
Using that example I was able to pass in two custom ComboBoxItem objects to a Timer.Elapsed event using lambda notation:
simulatorTimer.Elapsed +=
(sender, e) => onTimedEvent(sender, e,
(ComboBoxItem) cbPressureSetting.SelectedItem,
(ComboBoxItem) cbTemperatureSetting.SelectedItem);
and then into it's handler:
static void onTimedEvent(object sender, EventArgs e, ComboBoxItem pressure, ComboBoxItem temperature)
{
Console.WriteLine("Requested pressure: {0} PSIA\nRequested temperature: {1}° C", pressure, temperature);
}
This isn't any new code from the examples above, but it does demonstrate how to interpret them. Hopefully someone like me finds it instructive & useful so they don't spend hours trying to understand the concept like I did.
This code works in my project (except for a non-thread-safe exception with the ComboBoxItem objects that I don't believe changes how the example works). I'm figuring that out now.
Captured variables:
private void setup(string someData)
{
Object.assignHandler((sender,args) => {
evHandler(sender, someData);
});
}
public void evHandler(Object sender, string someData)
{
// use someData here
}
Or (C# 2.0 alternative):
Object.assignHandler((EventHandler)delegate(object sender,EventArgs args) {
evHandler(sender, someData);
});
you can try doing this:
string yourObject;
theClassWithTheEvent.myEvent += (sender, model) =>
{
yourObject = "somthing";
}
My question that was similar was marked a duplicate so thought I'd add an answer here since it won't let me on my question.
class Program
{
delegate void ComponentEventHandler(params dynamic[] args);
event ComponentEventHandler onTest;
static void Main(string[] args)
{
Program prg = new Program();
// can be bound to event and called that way
prg.onTest += prg.Test;
prg.onTest.Invoke("What", 5, 12.0);
Console.ReadKey();
}
public void Test(params dynamic[] values)
{
// assign our params to variables
string name = values[0];
int age = values[1];
double value = values[2];
Console.WriteLine(name);
Console.WriteLine(age);
Console.WriteLine(value);
}
}
Well, the simplest method id to make someData a member variable like so:
public class MyClass
{
private string _eventData;
private void setup(string someData)
{
_eventData = someData;
Object.assignHandler(evHandler);
}
public void evHandler()
{
// do something with _eventData here
}
}
I'm not sure that's the best way to do it, but it really depends on the event type, the object, etc.
You could create a custom object having additional properties based on Object:
class CustomObject : Object
{
public string SomeData;
}
private void setup(string someData)
{
CustomObject customObject = new CustomObject { SomeData = someData };
CustomObject.assignHandler(evHandler);
}
public void evHandler(Object sender)
{
string someData = ((CustomObject)sender).SomeData;
}
If the data should not be changed anymore after initialization, you could also add a custom constructor, for example.
Here is my one-line solution that pass extra parameters to a timer handler.
private void OnFailed(uint errorCode, string message)
{
ThreadPoolTimer.CreateTimer((timer) => {
UI.ErrorMessage = string.Format("Error: 0x{0:X} {1}", errorCode, message);
}, System.TimeSpan.FromMilliseconds(100));
}
This solution offers a way to pass extra parameters to an event handler while still allowing to unsubscibe:
Within the Subscribe() function of my example I create an Action that invokes a lambda function that supplies my event handler with the event args and my extra parameter. I then store this Action in a dictionary. When I want to unsubscribe, I can use the stored Actions to do so.
This works, I read the length of listeners before and after unsubscribing and it did decrease - you can unsubscribe again without problems.
public class Player
{
public Action<JumpInfo> OnJump;
}
public class PlayerJumpListener
{
public List<Player> MyPlayerList;
private Dictionary<Player, Action<JumpInfo>> _jumpActionsByPlayer = new Dictionary<Player, Action<JumpInfo>>();
private void Subscribe()
{
foreach (Player player in MyPlayerList)
{
Action<JumpInfo> playerJumpAction = (jumpInfo) => HandlePlayerJump(jumpInfo, player);
player.OnJump += playerJumpAction;
_jumpActionsByPlayer.Add(player, playerJumpAction);
}
}
private void Unsubscibe()
{
foreach (KeyValuePair<Player, Action<JumpInfo>> kvp in _jumpActionsByPlayer)
{
kvp.Key.OnJump -= kvp.Value;
}
}
private void HandlePlayerJump(JumpInfo jumpInfo, Player player)
{
// player jumped
}
}
I scoured the internet before a coworker kindly helped me, and boy I felt dumb. Brackets is the solution for the EventHandler.
Ex.
event EventHandler<(int, bool)> EventName;
and then pick it up with:
private void Delegate_EventName(object sender, (int, bool) e)
you can then access the info:
var temp = e.Item1;<br>
var temp2 = e.Item2;<br>
or you can add names as you would expect for parameters and call them via e:
private void Delegate_EventName(object sender, (int num, bool val) e)
you can then access the info:
var temp = e.num;
var temp2 = e.val;
Let's say I want to pass some extra data when assigning an event handler. Consider the following code:
private void setup(string someData)
{
Object.assignHandler(evHandler);
}
public void evHandler(Object sender)
{
// need someData here!!!
}
How would I go about getting someData into my evHandler method?
private void setup(string someData)
{
Object.assignHandler((sender) => evHandler(sender,someData));
}
public void evHandler(Object sender, string someData)
{
// need someData here!!!
}
I had a hard time figuring out #spender's example above especially with: Object.assignHandler((sender) => evHandler(sender,someData)); because there's no such thing as Object.assignHandler in the literal sense. So I did a little more Googling and found this example. The answer by Peter Duniho was the one that clicked in my head (this is not my work):
snip
The usual approach is to use an anonymous method with an event handler
that has your modified signature. For example:
void Onbutton_click(object sender, EventArgs e, int i) { ... }
button.Click += delegate(object sender, EventArgs e)
{ Onbutton_click(sender, e, 172); };
Of course, you don't have to pass in 172, or even make the third parameter
an int. :)
/snip
Using that example I was able to pass in two custom ComboBoxItem objects to a Timer.Elapsed event using lambda notation:
simulatorTimer.Elapsed +=
(sender, e) => onTimedEvent(sender, e,
(ComboBoxItem) cbPressureSetting.SelectedItem,
(ComboBoxItem) cbTemperatureSetting.SelectedItem);
and then into it's handler:
static void onTimedEvent(object sender, EventArgs e, ComboBoxItem pressure, ComboBoxItem temperature)
{
Console.WriteLine("Requested pressure: {0} PSIA\nRequested temperature: {1}° C", pressure, temperature);
}
This isn't any new code from the examples above, but it does demonstrate how to interpret them. Hopefully someone like me finds it instructive & useful so they don't spend hours trying to understand the concept like I did.
This code works in my project (except for a non-thread-safe exception with the ComboBoxItem objects that I don't believe changes how the example works). I'm figuring that out now.
Captured variables:
private void setup(string someData)
{
Object.assignHandler((sender,args) => {
evHandler(sender, someData);
});
}
public void evHandler(Object sender, string someData)
{
// use someData here
}
Or (C# 2.0 alternative):
Object.assignHandler((EventHandler)delegate(object sender,EventArgs args) {
evHandler(sender, someData);
});
you can try doing this:
string yourObject;
theClassWithTheEvent.myEvent += (sender, model) =>
{
yourObject = "somthing";
}
My question that was similar was marked a duplicate so thought I'd add an answer here since it won't let me on my question.
class Program
{
delegate void ComponentEventHandler(params dynamic[] args);
event ComponentEventHandler onTest;
static void Main(string[] args)
{
Program prg = new Program();
// can be bound to event and called that way
prg.onTest += prg.Test;
prg.onTest.Invoke("What", 5, 12.0);
Console.ReadKey();
}
public void Test(params dynamic[] values)
{
// assign our params to variables
string name = values[0];
int age = values[1];
double value = values[2];
Console.WriteLine(name);
Console.WriteLine(age);
Console.WriteLine(value);
}
}
Well, the simplest method id to make someData a member variable like so:
public class MyClass
{
private string _eventData;
private void setup(string someData)
{
_eventData = someData;
Object.assignHandler(evHandler);
}
public void evHandler()
{
// do something with _eventData here
}
}
I'm not sure that's the best way to do it, but it really depends on the event type, the object, etc.
You could create a custom object having additional properties based on Object:
class CustomObject : Object
{
public string SomeData;
}
private void setup(string someData)
{
CustomObject customObject = new CustomObject { SomeData = someData };
CustomObject.assignHandler(evHandler);
}
public void evHandler(Object sender)
{
string someData = ((CustomObject)sender).SomeData;
}
If the data should not be changed anymore after initialization, you could also add a custom constructor, for example.
Here is my one-line solution that pass extra parameters to a timer handler.
private void OnFailed(uint errorCode, string message)
{
ThreadPoolTimer.CreateTimer((timer) => {
UI.ErrorMessage = string.Format("Error: 0x{0:X} {1}", errorCode, message);
}, System.TimeSpan.FromMilliseconds(100));
}
This solution offers a way to pass extra parameters to an event handler while still allowing to unsubscibe:
Within the Subscribe() function of my example I create an Action that invokes a lambda function that supplies my event handler with the event args and my extra parameter. I then store this Action in a dictionary. When I want to unsubscribe, I can use the stored Actions to do so.
This works, I read the length of listeners before and after unsubscribing and it did decrease - you can unsubscribe again without problems.
public class Player
{
public Action<JumpInfo> OnJump;
}
public class PlayerJumpListener
{
public List<Player> MyPlayerList;
private Dictionary<Player, Action<JumpInfo>> _jumpActionsByPlayer = new Dictionary<Player, Action<JumpInfo>>();
private void Subscribe()
{
foreach (Player player in MyPlayerList)
{
Action<JumpInfo> playerJumpAction = (jumpInfo) => HandlePlayerJump(jumpInfo, player);
player.OnJump += playerJumpAction;
_jumpActionsByPlayer.Add(player, playerJumpAction);
}
}
private void Unsubscibe()
{
foreach (KeyValuePair<Player, Action<JumpInfo>> kvp in _jumpActionsByPlayer)
{
kvp.Key.OnJump -= kvp.Value;
}
}
private void HandlePlayerJump(JumpInfo jumpInfo, Player player)
{
// player jumped
}
}
I scoured the internet before a coworker kindly helped me, and boy I felt dumb. Brackets is the solution for the EventHandler.
Ex.
event EventHandler<(int, bool)> EventName;
and then pick it up with:
private void Delegate_EventName(object sender, (int, bool) e)
you can then access the info:
var temp = e.Item1;<br>
var temp2 = e.Item2;<br>
or you can add names as you would expect for parameters and call them via e:
private void Delegate_EventName(object sender, (int num, bool val) e)
you can then access the info:
var temp = e.num;
var temp2 = e.val;
I have a list of objects and I want to assign values in a factory method. I have checked similar questions but they have access to a method in existing assembly. I want to use a custom method and also define which event should be set.
for example
mylist.Assign(nameofevent,assignfuntion);
the usage something like
public static void Assign(this Control[] controls,eventhandler #event,Action func)
{
foreach (var item in controls)
//assign function to event
item.clicked += func;//Preferably clicked must be Specified from argument
}
Trying to help to get to the bottom of what is wrong with Shervin Ivari's question. I'm illustration how you can achieve it. But still unsure if this is what you want?
public static void Main()
{
var listeners = new List<SomeClassWithListener>
{
new SomeClassWithListener(),
new SomeClassWithListener(),
new SomeClassWithListener()
};
var theEvent = new SomeClassWithEvent();
MatchEmUp(listeners, theEvent);
theEvent.RaiseEvent();
}
public static void MatchEmUp(IEnumerable<SomeClassWithListener> listeners, SomeClassWithEvent theEvent)
{
foreach(SomeClassWithListener listener in listeners)
theEvent.ItsAlive += listener.ThenIllSlayIt;
}
public class SomeClassWithListener
{
public void ThenIllSlayIt(object sender, EventArgs e)
{
Console.WriteLine("Chaaaaaarge!");
}
}
public class SomeClassWithEvent
{
public EventHandler ItsAlive;
public void RaiseEvent()
{
ItsAlive.Invoke(this, new EventArgs());
}
}
https://dotnetfiddle.net/4Y13cf
Or by using delegates, EventHandler is also a delegate:
public static void Main()
{
var listener1 = new SomeClassWithListener();
var listener2 = new SomeClassWithListener();
var listener3 = new SomeClassWithListener();
var listeners = new List<EventHandler>
{
listener1.ThenIllSlayIt,
listener2.ThenIllSlayIt,
listener3.ThenIllSlayIt
};
var theEvent = new SomeClassWithEvent();
MatchEmUp(listeners, theEvent);
theEvent.RaiseEvent();
}
public static void MatchEmUp(IEnumerable<EventHandler> listeners, SomeClassWithEvent theEvent)
{
foreach(EventHandler listener in listeners)
theEvent.ItsAlive += listener;
}
public class SomeClassWithListener
{
public void ThenIllSlayIt(object sender, EventArgs e)
{
Console.WriteLine("Chaaaaaarge!");
}
}
public class SomeClassWithEvent
{
public EventHandler ItsAlive;
public void RaiseEvent()
{
ItsAlive.Invoke(this, new EventArgs());
}
}
https://dotnetfiddle.net/k16lsy
I am trying to create a tray icon in C#/.NET and so far I have this code that works:
....
Icon i = new Icon("favicon.ico");
ContextMenu cm = new ContextMenu();
ni.Icon = i;
MenuItem delMi = new MenuItem("Delete stuff");
MenuItem closeMi = new MenuItem("Close");
MenuItem testMi = new MenuItem("Test");
cm.MenuItems.Add(testMi);
cm.MenuItems.Add(delMi);
cm.MenuItems.Add(closeMi);
testMi.Click += TestMi_Click;
delMi.Click += DelMi_Click;
closeMi.Click += CloseMi_Click;
ni.ContextMenu = cm;
}
private void TestMi_Click(object sender, EventArgs e)
{
// Test event here
}
private void CloseMi_Click(object sender, EventArgs e)
{
// Close event here
}
private void DelMi_Click(object sender, EventArgs e)
{
// Delete event here
}
But I am trying to separate the code by having a function that returns an array of MenuItem instances, and having a loop that adds them to the ContextMenu, but I'm not sure how to add the click event handlers to the MenuItem instances in the loop:
....
Icon i = new Icon("favicon.ico");
ContextMenu cm = new ContextMenu();
ni.Icon = i;
MenuItem[] miArray = getArrayMI();
foreach(MenuItem mi in miArray)
{
cm.MenuItems.Add(mi);
//Not sure what to do here
mi.Click += mi
}
// How do I put this section into the loop instead
// of adding the event handlers one by one?
testMi.Click += TestMi_Click;
delMi.Click += DelMi_Click;
closeMi.Click += CloseMi_Click;
ni.ContextMenu = cm;
}
private MenuItem[] getArrayMI( )
{
MenuItem[] miArray = { new MenuItem("Delete stuff"), new MenuItem("Close"), new MenuItem("Test") };
return miArray;
}
private void TestMi_Click(object sender, EventArgs e)
{
// Test event here
}
private void CloseMi_Click(object sender, EventArgs e)
{
// Close event here
}
private void DelMi_Click(object sender, EventArgs e)
{
// Delete event here
}
The only thing I could think of would be to do something like this:
foreach(MenuItem mi in miArray)
{
cm.MenuItems.Add(mi);
mi.Click += mi.ToString() + "_Click";
}
I don't think it's a bad idea to abstract your original code, but I'd suggest looking at the abstraction in a different way. I'd recommend implementing some kind of separation of the view from the model - MVC, MVP, MVVM, etc. In this way, the code that actually happens when the click occurs is abstracted away from the view, into another layer of code.
For example, consider something like this (writing without an IDE so please forgive typos):
public interface IContextAction
{
string DisplayName { get; }
Action Invoke { get; }
}
public class WindowViewModel
{
public IEnumerable<IContextAction> ContextActions { get; private set; }
/* ... */
}
/* ... */
ContextMenu cm = new ContextMenu();
foreach (IContextAction action in viewModel.ContextActions)
{
MenuItem item = new MenuItem(action.DisplayName);
cm.MenuItems.Add(item);
item.Click += (sender,args) => action.Invoke();
}
I agree with the comment that suggests that, at least for the code example you posted, there is no need to "improve" the code. It's already a reasonable way to implement that particular logic. Furthermore, it is my preference to avoid relying on naming conventions to tie specific code to specific run-time objects. Doing so results in a fragile (i.e. easily broken) implementation, and restricts your ability to change the name of the code (e.g. to address some unrelated aspect of naming which would otherwise provide for more readable code).
That said, if you really want to do this, you can. Here is a Minimal, Complete, and Verifiable code example that illustrates how to create a delegate instance for an event handler based on the name of the object, and subscribe to the object's event:
class Program
{
static void Main(string[] args)
{
Class[] classInstances =
{
new Class("A"),
new Class("B"),
new Class("C"),
};
foreach (Class c in classInstances)
{
string methodName = c.Name + "_Event";
MethodInfo mi = typeof(Program).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
EventHandler handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), mi);
c.Event += handler;
}
foreach (Class c in classInstances)
{
c.RaiseEvent();
}
}
static void A_Event(object sender, EventArgs e) { Console.WriteLine("A_Event handler"); }
static void B_Event(object sender, EventArgs e) { Console.WriteLine("B_Event handler"); }
static void C_Event(object sender, EventArgs e) { Console.WriteLine("C_Event handler"); }
}
class Class
{
public string Name { get; }
public Class(string name)
{
Name = name;
}
public event EventHandler Event;
public void RaiseEvent()
{
Event?.Invoke(this, EventArgs.Empty);
}
}
Personally, I prefer a more explicit approach. That is, if there's really a need to encapsulate the assignment of handler to object in an abstracted way, to put this in explicit code. For example, provide a single event handler method to subscribe to all controls, and then have that method dispatch to the appropriate method by name:
static void Main(string[] args)
{
Class[] classInstances =
{
new Class("A"),
new Class("B"),
new Class("C"),
};
foreach (Class c in classInstances)
{
c.Event += All_Event;
}
foreach (Class c in classInstances)
{
c.RaiseEvent();
}
}
static void All_Event(object sender, EventArgs e)
{
switch (((Class)sender).Name)
{
case "A":
A_Event(sender, e);
break;
case "B":
B_Event(sender, e);
break;
case "C":
C_Event(sender, e);
break;
}
}
Alternatively, you can use a dictionary to represent the mapping from name to method:
static void Main(string[] args)
{
Class[] classInstances =
{
new Class("A"),
new Class("B"),
new Class("C"),
};
Dictionary<string, EventHandler> nameToHandler = new Dictionary<string, EventHandler>()
{
{ "A", A_Event },
{ "B", B_Event },
{ "C", C_Event },
};
foreach (Class c in classInstances)
{
c.Event += nameToHandler[c.Name];
}
foreach (Class c in classInstances)
{
c.RaiseEvent();
}
}
In both of those examples, you don't save any typing (the switch-based approach is particularly verbose), but it does move the object-to-handler relationship into its own area of the code, allowing it to be maintained more easily without having to deal with the event subscription itself.
If you really want a fully dynamic, reflection-based approach, I would opt for something more explicit and less fragile that relying on the method name. For example, you can create a custom attribute for the event handler methods, used to define what method goes with what object. This provides for a reasonably minimal amount of typing, but disconnects the method name from the mapping, so that you can go ahead and refactor the code to your heart's content without worrying about the event-handling aspect.
That would look something like this:
class Program
{
static void Main(string[] args)
{
Class[] classInstances =
{
new Class("A"),
new Class("B"),
new Class("C"),
};
Dictionary<string, EventHandler> nameToHandler =
(from mi in typeof(Program).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
let attribute = (Handler)mi.GetCustomAttribute(typeof(Handler))
where attribute != null
select new { attribute.Target, mi })
.ToDictionary(x => x.Target, x => (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), x.mi));
foreach (Class c in classInstances)
{
c.Event += nameToHandler[c.Name];
}
foreach (Class c in classInstances)
{
c.RaiseEvent();
}
}
[Handler("A")]
static void A_Event(object sender, EventArgs e) { Console.WriteLine("A_Event handler"); }
[Handler("B")]
static void B_Event(object sender, EventArgs e) { Console.WriteLine("B_Event handler"); }
[Handler("C")]
static void C_Event(object sender, EventArgs e) { Console.WriteLine("C_Event handler"); }
}
class Handler : Attribute
{
public string Target { get; }
public Handler(string target)
{
Target = target;
}
}
Given the following code:
public delegate void Signal();
public static class SignalExtensions
{
public static void SafeInvoke(this Signal signal)
{
Signal copy = signal;
if (copy != null)
{
copy();
}
}
}
public class RootEventSource
{
public event Signal RootEvent;
public void Raise()
{
this.RootEvent.SafeInvoke();
}
}
public class EventForwarder
{
private readonly RootEventSource rootEventSource;
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
// this is the critical part
this.rootEventSource.RootEvent
+= () => this.AnotherEvent.SafeInvoke();
}
public event Signal AnotherEvent;
// just an example of another method which is using the root event source
public override string ToString()
{
return this.rootEventSource.ToString();
}
}
class Program
{
static void Main(string[] args)
{
var rootEventSource = new RootEventSource();
var eventForwarder = new EventForwarder(rootEventSource);
eventForwarder.AnotherEvent += HandleAnotherEvent;
rootEventSource.Raise();
Console.WriteLine("done");
Console.ReadKey();
}
private static void HandleAnotherEvent()
{
Console.WriteLine("received AnotherEvent");
}
}
This results in the output:
received AnotherEvent
done
Now I make a slight change to the implementation of EventForwarder to use a method group for forwarding the event:
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
}
The output becomes:
done
So AnotherEvent is not raised.
Until now i would have considered the two lines:
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
this.rootEventSource.RootEvent += () => this.AnotherEvent.SafeInvoke();
as being equivalent. It seems they're not.
So what is the difference? Plus why is the event not being raised?
PS: while usually R# suggests to replace () => this.AnotherEvent.SafeInvoke(); by this.AnotherEvent.SafeInvoke it doesn't do so here. So apparently it knows that it should not do it here.
When you assign a method group to event like this:
this.rootEventSource.RootEvent += this.AnotherEvent.SafeInvoke;
you in fact create a delegate from method SignalExtensions.SafeInvoke which as a parameter takes your this.AnotherEventdelegate object. Since it is initially null, you create a delegate with null parameter. This null value will of course never change, since delegates are immutable.
If you want to forward an event you should maybe do it like this:
public class EventForwarder
{
private readonly RootEventSource rootEventSource;
public EventForwarder(RootEventSource rootEventSource)
{
this.rootEventSource = rootEventSource;
}
public event Signal AnotherEvent
{
add { this.rootEventSource.RootEvent += value; }
remove { this.rootEventSource.RootEvent -= value; }
}
}