I have an event in a loop. I am trying to prevent the same method being added to an event more than once. I've implemented the add and remove accessors.
However, I get an error stating that:
ItemsProcessed can only appear on the left hand side of += or -=
When I try to call them, even within the same class.
ItemsProcessed(this, new EventArgs()); // Produces error
public event EventHandler ItemsProcessed
{
add
{
ItemsProcessed -= value;
ItemsProcessed += value;
}
remove
{
ItemsProcessed -= value;
}
}
With an explicit event, you need to provide your own backing store - either a delegate field or something like EventHandlerList. The current code is recursive. Try:
private EventHandler itemsProcessed;
public event EventHandler ItemsProcessed
{
add
{
itemsProcessed-= value;
itemsProcessed+= value;
}
remove
{
itemsProcessed-= value;
}
}
Then (and noting I'm being a little cautious about the "about to turn null" edge-case re threading):
var snapshot = itemsProcessed;
if(snapshot != null) snapshot(this, EventArgs.Empty);
With more recent C# versions, this can be simplified:
itemsProcessed?.Invoke(this, EventArgs.Empty);
I can't tell from your post if you are trying to raise the event from a derived class or not, but one thing I've found is that you can't define an event in a base class and then raise it (directly) in a derived class, for some reason that isn't real clear to me yet.
So I define protected functions in base classes to raise events (that are defined in those base classes), like this:
// The signature for a handler of the ProgressStarted event.
// title: The title/label for a progress dialog/bar.
// total: The max progress value.
public delegate void ProgressStartedType(string title, int total);
// Raised when progress on a potentially long running process is started.
public event ProgressStartedType ProgressStarted;
// Used from derived classes to raise ProgressStarted.
protected void RaiseProgressStarted(string title, int total) {
if (ProgressStarted != null) ProgressStarted(title, total);
}
Then in the derived class, I call RaiseProgressStarted(title, total) instead of calling ProgressStarted(title, total).
It seems like kind of the long way around. Maybe someone else knows of a better way around this problem.
It seems that if you implement the EventHandler explicitly, you can't refer to the 'Property' when firing the event. You must refer to the backing store.
What error? I guess its stack overflow error, because you are calling add and remove on yourserlf (same event). Also you cannot raise event ACCESSOR.
Valid way to do this is to create backing private event, that will be added and removed to from public accessor, and you should raise this private event.
Dang, minute late.
Related
I am writing several UserControls for other programmers here.
Some Exposed EventHandlers with same name as the base are accepted
(UserControl CodesCombo -> SelectedValueChanged)
some aren't
(UserControl TextBox -> TextChanged)
I'm writing a UserControl which contains a TextBox. I need to expose the TextChanged event to the potential consumers of this control.
I have a similar control based on a ComboBox, and in that I used
public event EventHandler SelectedValueChanged
{
add { cbMain.SelectedValueChanged += value; }
remove { cbMain.SelectedValueChanged -= value; }
}
to expose the "Change" event "SelectedValueChanged", and it works without without any problems.
However, when I try to use this technique in my TextBox based control in a similar way
public event EventHandler TextChanged
{
add { tbMain.TextChanged += value; }
remove { tbMain.TextChanged -= value; }
}
I get a warning message:
'MyTextBox.TextChanged' hides inherited member 'UserControl.TextChanged'. Use new keyword if hiding was intentional.
I'm not entirely certain what the message means, other than the obvious, but what I do know is that I don't -think- I want to hide anything. I have internal SelectedValueChanged and TextChanged functions (cbMain_SelectedValueChanged, tbMain_TextChanged) which do a few things I need, but I want to allow the consumer to get an Event call on text change as well, just like they do in the ComboBox based one.
Also, I get no "change" event at all in the available list of event in the test program.
I've gotten around this for now by exposing the event as "TextChange"
new public event EventHandler TextChange
{
add { tbMain.TextChanged += value; }
remove { tbMain.TextChanged -= value; }
}
which gives me an event in the list and seems to work well enough, but I'd prefer to have a general solution to this as we're making several more controls along these lines for our package and I don't think I can get away with having names that are just "off".
Any idea what this message is really telling me, and how I can get my event internally, and also still get the user theirs?
Thanks!
Update: Asked for more specific code:
namespace NCFLSToolbox
{
public partial class NCFLSCodesCombo : UserControl
{
//Listed in the Events for System.Windows.Forms.ComboBox
private void cbMain_SelectedValueChanged(object sender, EventArgs e)
{
ControlRequiredColoring();
}
//Exposed Event for user
public event EventHandler SelectedValueChanged
{
add { cbMain.SelectedValueChanged += value; }
remove { cbMain.SelectedValueChanged -= value; }
}
}
public partial class NCFLSTextbox : UserControl
{
//Listed in the Events for System.Windows.Forms.TextBox
private void tbMain_TextChanged(object sender, EventArgs e)
{
ControlRequiredColoring();
}
//Couldn't expose "TextChanged" by name...
////public event EventHandler TextChanged
//// {
//// add { tbMain.TextChanged += value; }
//// remove { tbMain.TextChanged -= value; }
//// }
//...so I exposed "TextChange" instead.
public event EventHandler TextChange
{
add { tbMain.TextChanged += value; }
remove { tbMain.TextChanged -= value; }
}
}
}
Any idea what this message is really telling me?
First off, understand what it means to hide. Forget about the fact that it's an event for a moment. If we have:
class B { public void M() { Console.WriteLine("B.M()"); } }
class D : B { public void M() { Console.WriteLine("D.M()"); } }
then we have two methods both called M. If you say:
D d = new D();
B b = d;
d.M(); // D.M();
b.M(); // B.M();
The compiler is warning you that you have two methods, not one, and which one you get depends on the compile-time type of the receiver, NOT the run-time type of the receiver.
Why is that probably not what you want?
Three reasons.
First, many C# programmers come to C# from Java, where methods are automatically overridden. In C# methods have to be virtual to be overridden. The compiler is telling you that you are not getting the semantics you might expect.
Second, this is to mitigate the brittle base class problem. Suppose the author of B gives the D team a DLL assembly containing B without M, and D derives from it, adding method D.M. Then the B team realizes that they could implement M, so they add it to B and give the D team a new assembly. Now the D team should be warned that B.M exists, so that they can decide whether to delete D.M or not.
Third, why are you doing this in the first place? If the base class already has an event that you want, just use it! Don't make a new one that uses the old one; just use the old one. The compiler is telling you that you're definitely doing something strange and probably wrong.
If it is your intention to have two members on two different classes where one hides the other, you tell the compiler "I have thought about this and this is intentional" by putting new on the member. That is "this is a new member, and I meant to make it a new member, not an override of an old member".
The problem is that TextBox events do not propagate to the parent user control. If you wish them to do so, there are two ways to accomplish this:
1) Have the event on the user control ferry all additionals/removals to the event handler to the UserControl's event handler. This is roughly what your question is trying to do. However, I don't recommend it.
2) Have the event on the TextBox trigger events on the parent user control.
So, just run this code in your constructor, below InitializeComponent():
tbMain.TextChanged += (sender,e)=>OnTextChanged(e);
With this approach, a TextChanged event in the TextBox will call OnTextChanged, which raises a TextChanged event. You cannot invoke base class events directly (hence why the OnTextChanged method is provided).
Edit: tbMain.TextChanged += (sender,e)=>OnTextChanged(e); is semantically equivalent to the below code:
tbMain.TextChanged += OnTbMainTextChanged;
...
} //End of Constructor
private void OnTbMainTextChanged(object sender, EventArgs e)
{
OnTextChanged(e);
}
The benefit of using a lambda function is that it is more self-contained. Among other benefits, using a self-contained lambda function makes it obvious to future code maintainers that the sender is not being propagated, without requiring the maintainer to navigate to the named method. This is what you want: from the perspective of subscribers to the user control, your use of a TextBox to implement your control is an implementation detail.
Why can´t we raise an event with a custom implementation, while it is possible without them? See this code:
public class Program
{
private EventHandler myEvent;
public event EventHandler MyEvent
{
add { myEvent += value; }
remove { myEvent -= value; }
}
public event EventHandler AnotherEvent;
public static void Main()
{
var target = new Program();
target.MyEvent(null, null); // ERROR CS0079
target.AnotherEvent(null, null); // compiles
}
}
You see both events are declared within my class. While target.AnotherEvent(...) compiles just fine, target.MyEvent(...) does not:
The Event MyEvent can only appear on the left hand side of += or -=.
I Know an event is just a delegate with an add- and remove-method. So AnotherEvent is translated by the compiler to an add- and a remove-method:
private EventHandler _AnotherEvent;
public event EventHandler AnotherEvent
{
add { _AnotherEvent += value; }
remove { _AnotherEvent -= value; }
}
So I assume the call to AnotherEvent is replaced by the compiler to a call to the private delegate, which was _AnotherEvent(...).
Did I get this right? Are there any docs about why the second call works while the former does not? Or at least any description about what the compiler does here?
When an auto event is used public event EventHandler AnotherEvent;. The compiler will create a field (and some methods) for it and invoking is done on that field. So the public event does not exists anymore. It's syntactic sugar.
So invoking a non-auto event is not possible. Because it isn't found in the compiled code. It's replaced by add_, remove_ methods. You can only invoke on the private field (which is generated)
This explains why you cannot invoke an event outside the class instance.
It doesn't work because there is simply now way to get the actual invokeable event handler. As you have noted, there is just an add and remove, not a get.
The generated code for the event handler is:
.event [mscorlib]System.EventHandler MyEvent
{
.addon instance void ConsoleApp1.Program::add_MyEvent(class [mscorlib]System.EventHandler)
.removeon instance void ConsoleApp1.Program::remove_MyEvent(class [mscorlib]System.EventHandler)
} // end of event Program::MyEvent
It adds two method references, one for add and one for remove. If you look at it, how would it know what method to invoke? What if add and remove are much more complex than they are now? There is just no way to know for sure what event handler to call.
It's syntactical sugar. That you can call AnotherEvent like the backing field is a convenience provided by the compiler (AnotherEvent is a so-called field-like event). Once you add your own accessors, the event declaration ceases to be a field-like event and has to be invoked through its backing field.
See the relevant part of the C# Language Specification:
Field-like events
Within the program text of the class or struct that contains the declaration of an event, certain events can be used like fields. To be
used in this way, an event must not be abstract or extern, and must
not explicitly include event_accessor_declarations. Such an event can
be used in any context that permits a field. The field contains a
delegate (Delegates) which refers to the list of event handlers that
have been added to the event. If no event handlers have been added,
the field contains null.
(emphasis mine)
It is recommended that you lock the event before you add or remove a new event handler method.
saying that, have a look on this piece of code:
public event EventHandler MyEvent
{
add
{
lock (objectLock)
{
myEvent += value;
}
}
remove
{
lock (objectLock)
{
myEvent -= value;
}
}
}
The reason public event EventHandler AnotherEvent; works is because When no custom event accessors are supplied in your code, the compiler will add them automatically.
Follow this doc, How to: Implement Custom Event Accessors in order to get more details about the proper implementation and this post for another source.
Regarding the implementation:
private EventHandler myEvent;
public event EventHandler MyEvent
{
add
{
lock (objectLock)
{
myEvent += value;
}
}
remove
{
lock (objectLock)
{
myEvent -= value;
}
}
}
public event EventHandler AnotherEvent;
public static void Main()
{
var target = new Program();
var myEvent = target.MyEvent;
myEvent?.Invoke(EventArgs.Empty, EventArgs.Empty);
target.AnotherEvent(null, null);
}
Edit to explain the implementation:
var myEvent = target.MyEvent;
With an explicit event, you have to provide your own backing store - either a delegate field or something like EventHandlerList, so we just go with var here.
While copying code for RelayCommand from Josh Smith article I copied following code
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Then after reading this answer on SO I also copied in my class following code from DelegateCommand class of Prism.
protected void NotifyCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
But his gives me an error in NotifyCanExecuteChanged method
The event 'CanExecuteChanged' can only appear on the left hand side of += or -=
This error is not coming if I remove the add and remove overload from event. Can someone please help me understand the reason behind this?
With a field-like event (which is the name for the simple form without add/remove, then when you do if(CanExecuteChanged != null) or CanExecuteChanged(this, ...), the CanExecuteChanged is referring to the backing field, which is a delegate field of type EventHandler. You can invoke a delegate field. However, that is not the case in your example, because there is no obvious thing to invoke. There certainly isn't a local field, and the forwarded event (CommandManaged.RequerySuggested) does not intrinsically expose any "invoke" capability.
Basically, for that to work you would need access to an invoke mechanism. Most commonly, I would expect that to take the form of:
CommandManager.OnRequerySuggested();
but if there is a method that invokes this event (and there doesn't need to be), it could be called anything.
(the On* is a common pattern for a "raise this event" API, doubly-so if it is polymorphic)
I think you want CommandManager.InvalidateRequerySuggested. It forces the RequerySuggested event to be raised.
It seems like your class inherits from the class where event is declared. Event can be directly raised only in the base class, not in inherited class.
If you want to raise it in inherited class, write the following method in your base and call it from inherited class:
protected void RaiseMyEvent()
{
if (MyEvent != null)
{
MuEvent(this, args)
}
}
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 have declared a generic event handler
public delegate void EventHandler();
to which I have added the extension method 'RaiseEvent':
public static void RaiseEvent(this EventHandler self) {
if (self != null) self.Invoke();
}
When I define the event using the typical syntax
public event EventHandler TypicalEvent;
then I can call use the extension method without problems:
TypicalEvent.RaiseEvent();
But when I define the event with explicit add/remove syntax
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent {
add { _explicitEvent += value; }
remove { _explicitEvent -= value; }
}
then the extension method does not exist on the event defined with explicit add/remove syntax:
ExplicitEvent.RaiseEvent(); //RaiseEvent() does not exist on the event for some reason
And when I hover over to event to see the reason it says:
The event 'ExplicitEvent' can only
appear on the left hand side of += or
-=
Why should an event defined using the typical syntax be different from an event defined using the explicit add/remove syntax and why extension methods do not work on the latter?
EDIT: I found I can work around it by using the private event handler directly:
_explicitEvent.RaiseEvent();
But I still don't understand why I cannot use the event directly like the event defined using the typical syntax. Maybe someone can enlighten me.
When you create a "field-like" event, like this:
public event EventHandler Foo;
the compiler generates a field and an event. Within the source code of the class which declares the event, any time you refer to Foo the compiler understand that you're referring to the field. However, the field is private, so any time you refer to Foo from other classes, it refers to the event (and therefore the add/remove code).
If you declare your own explicit add/remove code, you don't get an auto-generated field. So, you've only got an event, and you can't raise an event directly in C# - you can only invoke a delegate instance. An event isn't a delegate instance, it's just an add/remove pair.
Now, your code contained this:
public EventHandler TypicalEvent;
This is slightly different still - it wasn't declaring an event at all - it was declaring a public field of the delegate type EventHandler. Anyone can invoke that, because the value is just a delegate instance. It's important to understand the difference between a field and an event. You should never write this kind of code, just as I'm sure you don't normally have public fields of other types such as string and int. Unfortunately it's an easy typo to make, and a relatively hard one to stop. You'd only spot it by noticing that the compiler was allowing you to assign or use the value from another class.
See my article on events and delegates for more information.
Because you can do this (it's non-real-world sample, but it "works"):
private EventHandler _explicitEvent_A;
private EventHandler _explicitEvent_B;
private bool flag;
public event EventHandler ExplicitEvent {
add {
if ( flag = !flag ) { _explicitEvent_A += value; /* or do anything else */ }
else { _explicitEvent_B += value; /* or do anything else */ }
}
remove {
if ( flag = !flag ) { _explicitEvent_A -= value; /* or do anything else */ }
else { _explicitEvent_B -= value; /* or do anything else */ }
}
}
How can the compiler know what it should do with "ExplicitEvent.RaiseEvent();"?
Answer: It can't.
The "ExplicitEvent.RaiseEvent();" is only syntax sugar, which can be predicated only if the event is implicitly implemented.
That's because you're not looking at it right.
The logic is the same as in Properties.
Once you've set the add/remove it's no longer an actual event, but a wrapper to expose the actual event (events can only be triggered from inside the class itself, so you always have access locally to the real event).
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent {
add { _explicitEvent += value; }
remove { _explicitEvent -= value; }
}
private double seconds;
public double Hours
{
get { return seconds / 3600; }
set { seconds = value * 3600; }
}
In both cases the member with the get/set or add/remove property doesn't really contain any data. You need a "real" private member to contain the actual data.
The properties just allow you program extra logic when exposing the members to outside world.
A good example for WHY you'd want to do it, is to stop extra computation when it's not needed (no one is listening to the event).
For example, lets say the events are triggered by a timer, and we don't want the timer to work if no-one is registered to the event:
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private EventHandler _explicitEvent;
public event EventHandler ExplicitEvent
{
add
{
if (_explicitEvent == null) timer.Start();
_explicitEvent += value;
}
remove
{
_explicitEvent -= value;
if (_explicitEvent == null) timer.Stop();
}
}
You'd probably want to lock the add/remove with an object (an afterthought)...
The "plain" declaration for TypicalEvent does some compiler trickery. It creates an event metadata entry, add and remove methods and a backing field. When your code refers to TypicalEvent, the compiler translates it into a reference to the backing field; when external code refers to TypicalEvent (using += and -=), the compiler translates it into a reference to the add or remove method.
The "explicit" declaration bypasses this compiler trickery. You are spelling out the add and remove methods and the backing field: indeed, as TcKs points out, there may not even be a backing field (this is a common reason for using the explicit form: see e.g. events in System.Windows.Forms.Control). Therefore the compiler can no longer quietly translate the reference to TypicalEvent into a reference to the backing field: if you want the backing field, the actual delegate object, you have to reference the backing field directly:
_explicitEvent.RaiseEvent()