This question already has answers here:
C# - Event keyword advantages?
(3 answers)
Closed 6 years ago.
I detected the feature of C# that it is possible to use an Action or Func like an event. What I mean is, that I can do following:
Action aAction;
aAction = DoSomething;
aAction += DoAnotherting;
// execute the action -> both functions will be executed
aAction();
aAction -= DoSomething; // unsubscribe on function
I was not aware of this, thought using += is only possible for events. In the first moment this looks quite well, as I do not have to use the event keyword and I can also call this action from outside of the owner class (what is not possible for events).
But I'm wondering, are there any good examples for such a use or is it only bad practice?
A complete example is shown here:
[TestMethod]
public void DummyTest()
{
DummyClass myInstance = new DummyClass();
int i = 0;
Action action1 = () => i++;
Action action2 = () => i += 2;
Func<int> func1 = () => 5;
myInstance.MyFunc += () => 3;
myInstance.MyFunc += func1;
Assert.AreEqual(5, myInstance.MyFunc?.Invoke() );
myInstance.MyFunc -= func1;
Assert.AreEqual(3, myInstance.MyFunc?.Invoke() );
myInstance.MyAction = action1;
myInstance.MyAction += action2;
myInstance.MyAction?.Invoke();
Assert.AreEqual(3, i);
myInstance.MyAction -= action1;
myInstance.MyAction?.Invoke();
Assert.AreEqual(5, i);
myInstance.MyAction = () => i = 0;
myInstance.MyAction?.Invoke();
Assert.AreEqual(0, i);
}
class DummyClass
{
public Action MyAction;
public Func<int> MyFunc;
}
It is my impression that the whole point of events is to put the control of events into the enclosing type. It is not up to clients to choose when the event is fired. An event is a (collection of) function(s) that is(/are) invoked when some state is changed in a type or when something interesting happens that clients might want to react to, but the exact details should be kept hidden for the same reason that you shouldn't expose fields to clients either.
There's nothing inherently HORRIBLE with it in the sense that it's going to blow up your house, but on the other hand there is no REASON to use them like this. Events are in the language for a reason, they have semantic meaning. If you use Action/Func delegates instead of events, people who read your code will have to figure out what the heck you're doing and why you aren't using the conventional tools instead. It's just clutter/noise, so my advise is to avoid it.
Related
I currently have code like this, an an async method:
MyEventHandler handler = (sender, e) => ...
Foo.My += handler;
await Foo.Bar(); // Fires event `My`
Foo.My -= handler;
I want to use a using statement to handle the before-and-after nature of this code. Perhaps somethign like:
using (ListenUntilDispose(Foo.My, handler))
{
await Foo.Bar();
}
But I can't figure out to write ListenTemporarily.
Is there a way to do this?
Yes, there is, but unfortunately it's not straightforward to do this generically, because events are not reified in C# - what I mean is, you can't pass around a reference to an event.
In the Reactive Extensions, this is worked around by using reflection, so you pass an event to a function by passing the object it is defined on, and the name of the event as a string:
using(ListenUntilDispose(Foo, "My", handler))
{
await Foo.Bar();
}
However, my reflection-fu is not terribly strong, and you also lose static type safety, meaning that the compiler can't check if handler really matches up with Foo.My. Here's another suggestion that is not as comfortable but might also suit you, if your goal really is "I want to use using" and not necessarily "I want the most easily readable solution":
class DelegateDisposable : IDisposable
{
private readonly Action m_dispose;
public DelegateDisposable(Action onDispose)
{
m_dispose = onDispose;
}
public void Dispose()
{
m_dispose();
}
}
Usage would be:
Foo.My += handler;
using(new DelegateDisposable(() => Foo.My -= handler))
{
await Foo.Bar();
}
Disclaimer: Written in the edit box, so untested :)
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.
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.
Consider
Action _captureAction;
private void TestSimpleCapturedAction()
{
Action action = new Action(delegate { });
Action printAction = () => Console.WriteLine("Printing...");
action += printAction;
CaptureActionFromParam(action);
action -= printAction;
_captureAction(); //printAction will be called!
}
private void CaptureActionFromParam(Action action)
{
_captureAction = () => action();
}
The reason printAction will be called by _captureAction is that the line
action -= printAction;
Actually translates into
action = (Action) Delegate.Remove(action, printAction);
so the action captured by _captureAction in CaptureActionFromParam() is not changed - only the local 'action' variable in TestSimpleCapturedAction() is affected.
My desired behavior in such a scenario would be printAction not being called. The only solution I can think of is defning a new "delegate container" class as such:
class ActionContainer
{
public Action Action = new Action(delegate { });
}
private void TestCapturedActionContainer()
{
var actionContainer = new ActionContainer();
Action printAction = () => Console.WriteLine("Printing...");
actionContainer.Action += printAction;
CaptureInvoker(actionContainer);
actionContainer.Action -= printAction;
_captureAction();
}
private void CaptureInvoker(ActionContainer actionContainer)
{
_captureAction = () => actionContainer.Action();
}
This works but I wonder if my desired behavior can be achieved without introducing this new layer of abstraction. Implementing the strategy pattern can easily lead to such a situation, so one would reckon the language and/or the BCL would support it natively somehow.
Thanks !
Delegates are like strings. They're implemented as reference types, but they behave more like immutable value types. When you add or subtract characters on a string, it doesn't change the string, it produces a new string that is the new result. When you add or subtract numbers from an integer, it doesn't change the integer, it produces a new integer that is the new result. And when you add or substract a delegate from a delegate, it doesn't change either delegate; it produces a new delegate which is the result.
If what you want to capture is a delegate which can vary then capture a variable that contains a reference to a delegate. Variables vary, that's why they're called "variables". If you want something that can vary, get the variable.
CaptureActionFromParam(()=>{action();});
Now the delegate that is captured has itself captured the variable "action", not the value that happens to be in it.
Remember:
Parameters are passed by value.
Lambdas capture variables, not values.
Make sense?
Is there any best practice with respect to coding style with respect to explicit use of the delegate keyword instead of using a lambda?
e.g.
new Thread(() =>
{
// work item 1
// work item 2
}).Start();
new Thread(delegate()
{
// work item 1
// work item 2
}).Start();
I think the lambda looks better. If the lambda is better style, what's the point of having a delegate keyword, other than for the fact that it existed before lambdas were implemented?
Lambda syntax is much more generalised, and the designers have said that they'd ideally remove the old overlapping syntaxes (dont have a citation, but it's probably Eric Lippert or Jon Skeet in a book or a podcast).
But delegate allows you to ignore parameters, e.g.:
object.Event += delegate { };
versus having to say:
object.Event += (sender,args) => { };
which can be very useful in large argument lists and/or to make the code more resilient to refactoring.
EDIT: As pointed out by Yann Schwartz in another answer (now unfortunately deleted), a very neat usage of this trick is in order to provide a default hander for an event using the Null Object pattern:-
class MyClassThatFiresWithoutTheTrick
{
public event EventHandler MyEvent; // implicit = null
// Need a method to keep this DRY as each fire requires a null check - see Framework Design Guidelines by Abrams and Cwalina
protected virtual void OnMyEvent()
{
// need to take a copy to avoid race conditions with _removes
// See CLR via C# 3rd edition p 264-5 for reason why this happens to work
//var handler = MyEvent;
// BUT THIS is the preferred version
var handler = Interlocked.CompareExchange( ref MyEvent, null, null);
// Need to do this check as it might not have been overridden
if( handler == null)
return;
handler( this, EventArgs.Empty );
}
}
class MyClassThatFiresWithTheTrick
{
public event EventHandler MyEvent = delegate{};
protected virtual void OnMyEvent()
{
MyEvent( this, EventArgs.Empty );
}
}
(though what you might often end up doing is an Inline Method of OnMyEvent, making the code even shorter again.)