I have the following class, which has one public event called LengthChanged:
class Dimension
{
public int Length
{
get
{
return this.length;
}
set
{
if (this.length != value)
{
this.length = value;
this.OnLengthChanged ();
}
}
protected virtual void OnLengthChanged()
{
var handler = this.LengthChanged;
if (handler != null)
{
handler (this, System.EventArgs.Empty);
}
}
public event System.EventHandler LengthChanged;
private int length;
}
I would like to be able to register/unregister handlers for this event in a method called Observer, which does not know anything about the Dimension class. I have come up with two scenarios, none of which are really satisfying:
Define an interface ILengthChanged with the LengthChanged event, then make sure Dimension implements ILengthChanged. Then I have to provide one implementation of the Observer method for every interface I define. This by no way generic enough. I'd really want to be able to simply pass in a reference to a System.EventHandler event.
Use System.Action<System.EventHandler> callbacks for registering and unregistering the event handler in the Observer method, just like that:
class Foo
{
public void Observer(System.Action<System.EventHandler> register,
System.Action<System.EventHandler> unregister)
{
register (this.MyEventHandler);
// keep track of the unregister callback, so that we can unregister
// our event handler later on, if needed...
}
private void MyEventHandler(object sender, System.EventArgs e)
{
...
}
}
which would then be invoked like this:
Foo foo = ...;
Dimension dim = ...;
foo.Observer (x => dim.LengthChanged += x, x => dim.LengthChanged -= x);
and which, when executed, will indeed end up wiring the LengthChanged event with the internal event handler MyEventHandler. But this is not very elegant. I would have loved to be able to write this instead:
Foo foo = ...;
Dimension dim = ...;
foo.Observer (dim.LengthChanged);
but I've no idea how this could be achieved. Maybe I am missing something really obvious here? I guess that some dynamic magic could do the trick, somehow, but this would not enforce compile-time type checking: I don't want the users of Observer to pass in references to events which do not satisfy the System.EventHandler event signature.
Unfortunately there isn't really a way of doing this. Events aren't first class citizens in .NET in general - although F# tries to promote them there.
Either pass in the subscribe/unsubscribe delegate or using a string indicating the name of the event. (The latter is often shorter, but obviously less safe at compile-time.)
Those are the approaches which Reactive Extensions takes - if there were a cleaner way of doing it, I'm sure they would be using that :(
You can create a custom accessor.
public event EventHandler NewEvent
{
add { Dimension.LengthChanged += value; }
remove { Dimension.LengthChanged -= value; }
}
Please see the documentation.
Event is not supposed to be passed into another method. However, you can pass delegate into another method. Perhaps, what you are looking for are just a simple public delegate instead of event.
If you change your event to this
public System.EventHandler LengthChanged;
You can simply pass the LengthChanged to Observer like this
Foo foo = ...;
Dimension dim = ...;
foo.Observer (dim.LengthChanged);
Related
This is abit difficult to word, so I am going to rely mostly on code.
BTW if you can word the question in a better light please dont hesitate giving your 2c!
class CustomEventArgs : EventArgs
{
public delegate void CustomEventHandler( Object sender, CustomEventArgs args );
public int data;
public CustomEventArgs (int _data)
{
data = _data;
}
}
This is the event that we will be using in this example.
class EventGenerator
{
public event CustomEventArgs.CustomEventHandler WeOccasion;
public EventGenerator ()
{
Task.Factory.StartNew( () =>
{
var index = 1;
// just loop and generate events every now and then
while (true)
{
Thread.Sleep( 1000 );
WeOccasion( this, new CustomEventArgs (++index));
}
});
}
}
This class just loops through firing off CustomEventHandler events.
class EventActivity
{
// EventActivity has an event of the same type as EventGenerator's
public event CustomEventArgs.CustomEventHandler WeOccasion;
// this is the part I cant seem to get right
public event CustomEventArgs.CustomEventHandler Source ( get; set; }
public bool Active {
set
{
if (value)
{
Source += DoWork;
}
else
{
Source -= DoWork;
}
}
}
private void DoWork( Object sender, CustomEventArgs frame);
}
Here is where I really need help. I want almost a pointer to an event in an another class of type CustomEventHandler that I can later assign event handlers to when I activate the activity.
Here is a usage example wrapped in a class;
class EventAssigner
{
EventGenerator Generator;
EventActivity DoSomeThing1;
EventActivity DoSomeThing2;
public EventAssigner ()
{
// init
Generator = new EventGenerator();
DoSomeThing1 = new EventActivity();
DoSomeThing2 = new EventActivity();
// assign sources
DoSomeThing1.Source = Generator.WeOccasion;
DoSomeThing2.Source = DoSomeThing1.WeOccasion;
// activate the first activity
DoSomeThing1.Active = true;
}
public void Activate2()
{
// activate the second activity
DoSomeThing2.Active = true;
}
public void Deactivate2()
{
// deactivate the second activity
DoSomeThing2.Active = false;
}
}
Obiously this code doesnt work, and I suppose thats what I am asking. Can you get this design pattern to work?
What you're asking to do isn't really possible with .NET events, and probably isn't as desirable as you might think. A bit of background should help explain why:
Properties have a basic pattern with get and set operations. These are invoked by accessing the property (for a get) and an assignment to the property (for a set):
var x = instance.Prop1; // access
instance.Prop1 = x; // assignment
When you access an event from outside the class (i.e. instance.Event) you are given the "public" face, which, like properties, has two operations: add handler and remove handler. These are invoked using the += and -= operators.
instance.Event += this.Handler; // add
instance.Event -= this.Handler; // remove
The important thing to notice that it doesn't have a "get" operation - there is no way to get a reference to the event outside the class; you can only modify the handlers registered.
When you access an event from within a class, you are given the "private" face, which is essentially a special collection of delegates (function pointers) to the registered event handlers. When you invoke the delegate, you're actually asking the framework to iterate through the registered event handlers and invoke those.
if(this.Event != null)
{
this.Event.Invoke(e, args); // raise event
}
This separation of public face and private face is what allows you have a nice simple event keyword which magically gives you an event. It is also what stops you passing a reference to the event around.
To pass the event into registration methods, you have to pass the object the event is attached to. If you have multiple classes which implement the same event and you want to register them all in the same way, you should have them implement an interface with the event (yes, events can be on interfaces) and write your method to accept the interface as an argument.
If I'm reading you correct, you want the line
DoSomeThing1.Source = Generator.WeOccasion;
to save the pointer to the WeOccasion event, so that you can add the DoWork call to it later, right?
I don't think that is possible with "normal" code, as the event is not a value, but rather like a property. Consider the following analogous code:
myProp = aPerson.Name; // attempt to save the name property for later
myProp = "Fred"; // intent is to set aPerson.Name = "Fred"
If you want this to work I'd suggest using reflection to find the event, and add to it using the EventInfo.AddEventHandler method (http://msdn.microsoft.com/en-us/library/system.reflection.eventinfo.addeventhandler.aspx)
Why isn't it possible to assign events along with properties in object initializers in C#? It seems to be so natural to do so.
var myObject = new MyClass()
{
Property = value,
Event1 = actor,
// or
Event2 += actor
};
Or is there some trick that I don't know of?
This didn't make C# 6 or C# 7 (since the original question), however it hasn't been decided against. There is an issue on GitHub that tracks the language proposal. You can vote for it there, as well as follow links into previous discussion around the feature.
https://github.com/dotnet/csharplang/issues/307
If you'd like to see this feature, add a thumbs-up to the issue to help raise its visibility.
The proposed syntax is:
var timer = new DispatcherTimer {
Tick += delegate {},
Interval = TimeSpan.FromSeconds(1d),
};
As far the external contract is concerned, an event doesn't have a setter, only add and remove methods - subscribers can register and unregister from the event, and the publishing object decides when to invoke the callbacks by 'raising' the event. Consequently, the idea of "assigning an event", in general, is meaningless.
However, when you declare an event in a class, the C# compiler provides you with what is really a convenience-feature: when you don't provide your own implementation, it creates a private, backing delegate-field for you, along with the appropriate add / remove implementations . This allows you to "set the event" (really the backing field) within the class, but not outside it. To understand this, consider:
public class Foo
{
// implemented by compiler
public event EventHandler MyEvent;
public static Foo FooFactory(EventHandler myEventDefault)
{
// setting the "event" : perfectly legal
return new Foo { MyEvent = myEventDefault };
}
}
public class Bar
{
public static Foo FooFactory(EventHandler myEventDefault)
{
// meaningless: won't compile
return new Foo { MyEvent = myEventDefault };
}
}
public class Baz
{
// custom implementation
public event EventHandler MyEvent
{
add { } // you can imagine some complex implementation here
remove { } // and here
}
public static Baz BazFactory(EventHandler myEventDefault)
{
// also meaningless: won't compile
return new Baz { MyEvent = myEventDefault };
}
}
You can only use the operators += or -= to an event outside its owner class.
public class Data
{
public event EventHandler OnSave = (s,e) =>
{
//do something important!
};
public void Save()
{
OnSave(this,null);
//do save
}
}
//outside the class
Data data = new Data { OnSave = null }; //compile error
data.OnSave = SomeMethodElse; //compile error
data.OnSave += MyCustomActionsOnSave; //ok
data.Save();
You can't remove the OnSave action defined in the class. You can only add/remove your own OnSave actions outside the class. If you remove the event keyword, the OnSave will be no longer an event, but an ordinary delegate. Then you can do anything including assigning value outside the class.
I'm currently developing a tiny technical Framework that is independant of any applications. Business code just refers to this Framework.
According this article : http://msdn.microsoft.com/en-us/library/5z57dxz2.aspx (exemple 2), we need to provide a delegate for the custom event.
Problem is, anyone can Invoke my handler (and then raise the event), even in my Business Code and that isn't logical for me, so what is the best way to raise a custom Event with a delegate that is only "internal" and not "public" ?
Thanks for help.
I am not sure if I get it right or not. I think that you feel like if you provide a public Delegate type for your custom event, anyone will be able to Raise that event.
Well, that is not true. Only the class that defines that custom event can raise it. If this is your issue, don't worry.
Not true. It's not allowed to invoke an event outside the class which the event belongs to. Others can only use += and -= operators to your event. Only in the class, you can invoke the event. That is a difference between an event and a normal delegate. That is:
public Data
{
public event EventHandler OnSave
public EventHandler OnLoad;
private void Load()
{
if (OnLoad!=null) OnLoad();
//other operations
}
private void Save()
{
if (OnSave!=null) OnSave();
//other operations
}
}
And outside the class:
Data data = new Data();
data.OnLoad += (s,e) => {};
data.OnSave += (s,e) => {};
data.OnLoad = (s,e)=>{};
//data.OnSave = (s,e)=>{}; //invalid
data.OnLoad();
//data.OnSave(); //invalid
The delegate is just a type declaration describing the "signature" of your event. This has to be public. To actually invoke your event you often implement a method named OnEvent (where you substitute Event with Click or Closed or whatever describes your event). This method should be private (or protected) in your class.
You cannot declare an event using a delegate that is less "visible" than the event.
Problem is, anyone can Invoke my handler (and then raise the event), even in my Business Code
That isn't true. You declare an event as follows:
public event FooEventHandler Foo;
The only thing that external code can do with the event is subscribe to it (+=), or unsubscribe from it (-=). It can't access the actual delegate, which is generated by the compiler as a private field. In other words, this code would be invalid :
SomeClass x = new SomeClass();
x.Foo(x, new FooEventArgs()); // compilation error here
Don't forget that an event is actually a pair of methods (add and remove). The compiler rewrites the event declaration to something along those lines:
private FooEventHandler _foo;
public event FooEventHandler Foo
{
add { _foo += value; }
remove { _foo -= value; }
}
(the generated code is actually a bit more complex, with some locking to ensure thread safety)
As you can see, the _foo field is private, so client code can't access it. Only the event's add and remove accessors are accessible.
One way of doing it:
Instead of public event, create a method that will manually subscribe your desired delegates, and store them in `private List _delegates' field.
Then, from the 'inside', call each of them when you desire.
public class Framework
{
public delegate void Method();
public void AttachEvent(Method M)
{
_methods.Add(M);
}
private List<Method> _methods;
private FireMethods()
{
_methods.Foreach(x=>x.Invoke());
}
}
Or, you can embrace 'by design' feature of the events that they aren't publicly Invoke()-able.
:)
I would like to ensure that I only subscribe once in a particular class for an event on an instance.
For example I would like to be able to do the following:
if (*not already subscribed*)
{
member.Event += new MemeberClass.Delegate(handler);
}
How would I go about implementing such a guard?
I'm adding this in all the duplicate questions, just for the record. This pattern worked for me:
myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;
Note that doing this every time you register your handler will ensure that your handler is registered only once.
If you are talking about an event on a class that you have access to the source for then you could place the guard in the event definition.
private bool _eventHasSubscribers = false;
private EventHandler<MyDelegateType> _myEvent;
public event EventHandler<MyDelegateType> MyEvent
{
add
{
if (_myEvent == null)
{
_myEvent += value;
}
}
remove
{
_myEvent -= value;
}
}
That would ensure that only one subscriber can subscribe to the event on this instance of the class that provides the event.
EDIT please see comments about why the above code is a bad idea and not thread safe.
If your problem is that a single instance of the client is subscribing more than once (and you need multiple subscribers) then the client code is going to need to handle that. So replace
not already subscribed
with a bool member of the client class that gets set when you subscribe for the event the first time.
Edit (after accepted): Based on the comment from #Glen T (the submitter of the question) the code for the accepted solution he went with is in the client class:
if (alreadySubscribedFlag)
{
member.Event += new MemeberClass.Delegate(handler);
}
Where alreadySubscribedFlag is a member variable in the client class that tracks first subscription to the specific event.
People looking at the first code snippet here, please take note of #Rune's comment - it is not a good idea to change the behavior of subscribing to an event in a non-obvious way.
EDIT 31/7/2009: Please see comments from #Sam Saffron. As I already stated and Sam agrees the first method presented here is not a sensible way to modify the behavior of the event subscription. The consumers of the class need to know about its internal implementation to understand its behavior. Not very nice.
#Sam Saffron also comments about thread safety. I'm assuming that he is referring to the possible race condition where two subscribers (close to) simultaneously attempt to subscribe and they may both end up subscribing. A lock could be used to improve this. If you are planning to change the way event subscription works then I advise that you read about how to make the subscription add/remove properties thread safe.
As others have shown, you can override the add/remove properties of the event. Alternatively, you may want to ditch the event and simply have the class take a delegate as an argument in its constructor (or some other method), and instead of firing the event, call the supplied delegate.
Events imply that anyone can subscribe to them, whereas a delegate is one method you can pass to the class. Will probably be less surprising to the user of your library then, if you only use events when you actually want the one-to-many semantics it usually offers.
You can use Postsharper to write one attribute just once and use it on normal Events. Reuse the code. Code sample is given below.
[Serializable]
public class PreventEventHookedTwiceAttribute: EventInterceptionAspect
{
private readonly object _lockObject = new object();
readonly List<Delegate> _delegates = new List<Delegate>();
public override void OnAddHandler(EventInterceptionArgs args)
{
lock(_lockObject)
{
if(!_delegates.Contains(args.Handler))
{
_delegates.Add(args.Handler);
args.ProceedAddHandler();
}
}
}
public override void OnRemoveHandler(EventInterceptionArgs args)
{
lock(_lockObject)
{
if(_delegates.Contains(args.Handler))
{
_delegates.Remove(args.Handler);
args.ProceedRemoveHandler();
}
}
}
}
Just use it like this.
[PreventEventHookedTwice]
public static event Action<string> GoodEvent;
For details, look at Implement Postsharp EventInterceptionAspect to prevent an event Handler hooked twice
You would either need to store a separate flag indicating whether or not you'd subscribed or, if you have control over MemberClass, provide implementations of the add and remove methods for the event:
class MemberClass
{
private EventHandler _event;
public event EventHandler Event
{
add
{
if( /* handler not already added */ )
{
_event+= value;
}
}
remove
{
_event-= value;
}
}
}
To decide whether or not the handler has been added you'll need to compare the Delegates returned from GetInvocationList() on both _event and value.
I know this is an old Question, but the current Answers didn't work for me.
Looking at C# pattern to prevent an event handler hooked twice (labelled as a duplicate of this question), gives Answers that are closer, but still didn't work, possibly because of multi-threading causing the new event object to be different or maybe because I was using a custom event class. I ended up with a similar solution to the accepted Answer to the above Question.
private EventHandler<bar> foo;
public event EventHandler<bar> Foo
{
add
{
if (foo == null ||
!foo.GetInvocationList().Select(il => il.Method).Contains(value.Method))
{
foo += value;
}
}
remove
{
if (foo != null)
{
EventHandler<bar> eventMethod = (EventHandler<bar>)foo .GetInvocationList().FirstOrDefault(il => il.Method == value.Method);
if (eventMethod != null)
{
foo -= eventMethod;
}
}
}
}
With this, you'll also have to fire your event with foo.Invoke(...) instead of Foo.Invoke(...). You'll also need to include System.Linq, if you aren't already using it.
This solution isn't exactly pretty, but it works.
I did this recently and I'll just drop it here so it stays:
private bool subscribed;
if(!subscribed)
{
myClass.MyEvent += MyHandler;
subscribed = true;
}
private void MyHandler()
{
// Do stuff
myClass.MyEvent -= MyHandler;
subscribed = false;
}
Invoke only distinct elements from GetInvocationList while raising:
using System.Linq;
....
public event HandlerType SomeEvent;
....
//Raising code
foreach (HandlerType d in (SomeEvent?.GetInvocationList().Distinct() ?? Enumerable.Empty<Delegate>()).ToArray())
d.Invoke(sender, arg);
Example unit test:
class CA
{
public CA()
{ }
public void Inc()
=> count++;
public int count;
}
[TestMethod]
public void TestDistinctDelegates()
{
var a = new CA();
Action d0 = () => a.Inc();
var d = d0;
d += () => a.Inc();
d += d0;
d.Invoke();
Assert.AreEqual(3, a.count);
var l = d.GetInvocationList();
Assert.AreEqual(3, l.Length);
var distinct = l.Distinct().ToArray();
Assert.AreEqual(2, distinct.Length);
foreach (Action di in distinct)
di.Invoke();
Assert.AreEqual(3 + distinct.Length, a.count);
}
[TestMethod]
public void TestDistinctDelegates2()
{
var a = new CA();
Action d = a.Inc;
d += a.Inc;
d.Invoke();
Assert.AreEqual(2, a.count);
var distinct = d.GetInvocationList().Distinct().ToArray();
Assert.AreEqual(1, distinct.Length);
foreach (Action di in distinct)
di.Invoke();
Assert.AreEqual(3, a.count);
}
I'm using C#, .NET 3.5. I understand how to utilize events, how to declare them in my class, how to hook them from somewhere else, etc. A contrived example:
public class MyList
{
private List<string> m_Strings = new List<string>();
public EventHandler<EventArgs> ElementAddedEvent;
public void Add(string value)
{
m_Strings.Add(value);
if (ElementAddedEvent != null)
ElementAddedEvent(value, EventArgs.Empty);
}
}
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
MyList tmp = new MyList();
tmp.ElementAddedEvent += new EventHandler<EventArgs>(Fired);
tmp.Add("test");
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
However, what I do not understand, is when one declares an event handler
public EventHandler<EventArgs> ElementAddedEvent;
It's never initialized - so what, exactly, is ElementAddedEvent? What does it point to? The following won't work, because the EventHandler is never initialized:
[TestClass]
public class TestMyList
{
private bool m_Fired = false;
[TestMethod]
public void TestEvents()
{
EventHandler<EventArgs> somethingHappend;
somethingHappend += new EventHandler<EventArgs>(Fired);
somethingHappend(this, EventArgs.Empty);
Assert.IsTrue(m_Fired);
}
private void Fired(object sender, EventArgs args)
{
m_Fired = true;
}
}
I notice that there is an EventHandler.CreateDelegate(...), but all the method signatures suggest this is only used for attaching Delegates to an already existing EventHandler through the typical ElementAddedEvent += new EventHandler(MyMethod).
I'm not sure if what I am trying to do will help... but ultimately I'd like to come up with an abstract parent DataContext in LINQ whose children can register which table Types they want "observed" so I can have events such as BeforeUpdate and AfterUpdate, but specific to types. Something like this:
public class BaseDataContext : DataContext
{
private static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> m_ObservedTypes = new Dictionary<Type, Dictionary<ChangeAction, EventHandler>>();
public static void Observe(Type type)
{
if (m_ObservedTypes.ContainsKey(type) == false)
{
m_ObservedTypes.Add(type, new Dictionary<ChangeAction, EventHandler>());
EventHandler eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Insert, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Update, eventHandler);
eventHandler = EventHandler.CreateDelegate(typeof(EventHandler), null, null) as EventHandler;
m_ObservedTypes[type].Add(ChangeAction.Delete, eventHandler);
}
}
public static Dictionary<Type, Dictionary<ChangeAction, EventHandler>> Events
{
get { return m_ObservedTypes; }
}
}
public class MyClass
{
public MyClass()
{
BaseDataContext.Events[typeof(User)][ChangeAction.Update] += new EventHandler(OnUserUpdate);
}
public void OnUserUpdated(object sender, EventArgs args)
{
// do something
}
}
Thinking about this made me realize I don't really understand what's happening under the hod with events - and I would like to understand :)
I've written this up in a fair amount of detail in an article, but here's the summary, assuming you're reasonably happy with delegates themselves:
An event is just an "add" method and a "remove" method, in the same way that a property is really just a "get" method and a "set" method. (In fact, the CLI allows a "raise/fire" method as well, but C# never generates this.) Metadata describes the event with references to the methods.
When you declare a field-like event (like your ElementAddedEvent) the compiler generates the methods and a private field (of the same type as the delegate). Within the class, when you refer to ElementAddedEvent you're referring to the field. Outside the class, you're referring to the field.
When anyone subscribes to an event (with the += operator) that calls the add method. When they unsubscribe (with the -= operator) that calls the remove.
For field-like events, there's some synchronization but otherwise the add/remove just call Delegate.Combine/Remove to change the value of the auto-generated field. Both of these operations assign to the backing field - remember that delegates are immutable. In other words, the autogenerated code is very much like this:
// Backing field
// The underscores just make it simpler to see what's going on here.
// In the rest of your source code for this class, if you refer to
// ElementAddedEvent, you're really referring to this field.
private EventHandler<EventArgs> __ElementAddedEvent;
// Actual event
public EventHandler<EventArgs> ElementAddedEvent
{
add
{
lock(this)
{
// Equivalent to __ElementAddedEvent += value;
__ElementAddedEvent = Delegate.Combine(__ElementAddedEvent, value);
}
}
remove
{
lock(this)
{
// Equivalent to __ElementAddedEvent -= value;
__ElementAddedEvent = Delegate.Remove(__ElementAddedEvent, value);
}
}
}
The initial value of the generated field in your case is null - and it will always become null again if all subscribers are removed, as that is the behaviour of Delegate.Remove.
If you want a "no-op" handler to subscribe to your event, so as to avoid the nullity check, you can do:
public EventHandler<EventArgs> ElementAddedEvent = delegate {};
The delegate {} is just an anonymous method which doesn't care about its parameters and does nothing.
If there's anything that's still unclear, please ask and I'll try to help!
Under the hood, events are just delegates with special calling conventions. (For example, you don't have to check for nullity before raising an event.)
In pseudocode, Event.Invoke() breaks down like this:
If Event Has Listeners
Call each listener synchronously on this thread in arbitrary order.
Since events are multicast, they will have zero or more listeners, held in a collection. The CLR will loop through them, calling each in an arbitrary order.
One big caveat to remember is that event handlers execute in the same thread as the event is raised in. It's a common mental error to think of them as spawning a new thread. They do not.