I have a C# class which contains the following:
public event Action<int> UnsupportedMessage;
It also has a method containing
if (template != null)
{
...
}
else
{
if (UnsupportedMessage != null)
{
UnsupportedMessage(templateId);
}
}
Another C# class called HealthCheck contains public void MyMessage(int id).
What's supposed to happen is if template is null, an event should call HealthCheck.MyMessage().
So many things are wrong here. UnsupportedMessage is always null so UnsupportedMessage(templateId); never gets executed. Even is I remove if (UnsupportedMessage != null) UnsupportedMessage(templateId); will throw System.NullReferenceException: 'Object reference not set to an instance of an object.'. Even if it did work, there's no reference to HealthCheck.MyMessage() so I don;t know how it could ever be run.
How do I create an event to do what I need?
Update
I tried changing to UnsupportedMessage?.Invoke(templateId); as mentioned in the below answer and added this in the line above it:
UnsupportedMessage += _handlerClass.UnsupportedMessage;
_handlerClass is initialized by the constructor contains this method:
public void UnsupportedMessage(int value)
{
...
}
This initially seemed to work but it seems to be getting called many more times than I expected.
It sounds like you're simply missing the event subscription step; you'd presumably need:
objectThatIsGoingToRaiseTheEvent.UnsupportedMessage += healthCheckInstance.MyMessage;
which will make UnsupportedMessage become non-null, and make everything work. You should also change:
if (UnsupportedMessage != null)
{
UnsupportedMessage(templateId);
}
to either
UnsupportedMessage?.Invoke(templateId);
or (on older C# versions, noting that this is semantically identical)
var handler = UnsupportedMessage;
if (handler != null) { handler(templateId); }
to avoid a race condition that is possible if the last event-handler unsubscribes (-=) between the two reads of UnsupportedMessage in the original version.
Related
I am attempting to bind a TextBox for error logging in Windows Forms.
I bind the TextBox like this:
this.operationEventHistoryTextbox.DataBindings.Add("Text", this.Logger, "OperationLog")
The Logger object is an instance of the Logger class, which contains the following property.
public string OperationLog
{
get
{
return this.operationLog;
}
set
{
if (this.operationLog != value)
{
this.operationLog = value;
System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new System.ComponentModel.PropertyChangedEventArgs("OperationLog"));
}
}
}
}
And I get the error when calling this.Logger.LogEvent("message"). My LogEvent method contains the following:
public void LogEvent(string msg)
{
this.OperationLog += System.DateTime.Now + ": " + msg + "\r\n";
}
The InvalidOperationException says that there was an invalid Cross-thread operation, and that the Control operationEventHistoryTextBox was accessed from a thread other than the thread it was created on.
I understand this to mean that I've written parts of my code in a way that wasn't thread-safe, but I don't really understand why, or what to fix.
I could just go around setting all of these functions to invoke rather than be directly called, but I'd like really understand what isn't working.
Update: I've attempted to use System.Threading.ScynchronizationContext to raise the PropertyChanged Event on the correct thread, however, I continue to get the error. Here is the new setter for the property:
set
{
if (this.operationLog != value)
{
System.Threading.SynchronizationContext context = System.Threading.SynchronizationContext.Current
?? new System.Threading.SynchronizationContext();
this.operationLog = value;
context.Send(
(s) =>
{
System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new System.ComponentModel.PropertyChangedEventArgs("OperationLog"));
}
},
null);
}
}
Am I not correctly creating the SynchronizationContext? Or is there something else at work here?
Update 2:
If I replace the call of handler(this, ... ) with handler(null, ... ) or handler(this.OperationLog), the setter will run without errors, but does not actually update the text.
For now I'm using a workaround where I will, instead of using a DataBinding to link the text, just manually do that by adding my own handler to the PropertyChanged Event.
The problem seems to be that you are calling the LogEvent method from a background thread. Because of the databinding, the text box is then being updated from that background thread resulting in the exception.
The solution is to make sure that either the LogEvent method is always executing on the UI thread or - better - the OperationLog setter.
You trying update view from another thread. Textbox.Text can't be set from other thread then UI thread
Ok, I've found workaround that is almost a solution.
I've done the following:
set
{
if (this.operationLog != value)
{
this.operationLog = value;
System.ComponentModel.PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
System.Action a = () => handler(this, new PropertyChangedEventArgs("OperationLog"));
System.Windows.Forms.Form.ActiveForm.Invoke(a);
}
}
}
This guarantees that the event is raised on the UI thread, and allows it to update the textbox safely. However, to me, it doesn't feel quite right. While I've done what it needs, there is something that feels like it doesn't fit the Model-View separation that I was looking for. Be that as it may, it is certainly better than manually going operationEventHistoryTextbox.Invoke( ... ).
An interesting drawback is that this can cause errors when the active form does not share threads with what you are working on -- this will include forms in other processes, so alt tabbing will not be allowed.
EDIT: I've found a fix to that problem. Instead of using System.Windows.Forms.Form.ActiveForm, I used System.Windows.Forms.Application.OpenForms[0] to reference the first form within the app. This gives me the context to run an invoke or begin invoke.
I am always confused about when to use if (sth == null). Design of delegate in C# is like this:
//Declaration:
private delegate void OnSthHappened();
//In some place, do:
OnSthHappened += SthHappendCallback;
//When use delegate:
if(OnSthHappened != null)
OnSthHappened();
I really think of judging it is null everytime when use OnSthHappened is unnecessary. I know that in compiler, delegate will become a class to handle the callback. Then why the C# compiler doesn't do the other way like:
//Use the delegate directly:
OnSthHappened();
//In the created-by-compiler class, do the judgement:
//object: instance object, Intptr method: a method pointer
if(method != 0x00) //Null, hide the judgement here
{
Invoke(object, method);
}
I just gave a scene of where to judge null, this is giving me hard choices everytime when I try to write API, to do ==null in caller or called functions? Does anyone could give me some style about where to use ==null judgement?
I know that in compiler, delegate will become a class to handle the callback
Delegate (more precise, MulticastDelegate, because in fact we work with multicast delegates) is a class already. The delegate keyword is just a way to declare a MulticastDelegate-derived type.
Where to use if(obj == null) When writing API?
Shortly, check for null:
in public or protected contacts, if the particular method parameter or return value must not be null. If it is null, throw an exception;
in public, protected or private contracts, when value the value could be null, and you can handle that.
public void Method1(Action action)
{
Contact.Requires<ArgumentNullException>(action != null);
}
public object Method2()
{
Contract.Ensures(Contract.Result<object>() != null);
// code here
}
public void Method2(Action action == null)
{
if (action != null)
action();
}
Do not check for null:
in private contacts, when value must not be null.
private void Method3()
{
var result = Method2();
// In case of result == null, NRE is a best option here.
Console.WriteLine(result.ToString().Length);
}
UPD.
why use different strategies between private and public methods? What is the benefit?
Public or protected surface is unpredictable.
You can't guarantee, that user of your class will provide, for example, valid parameter values. That's why you must check them - this is the way to tell the user, that something is wrong in his code. By delivering understandable exception, you can tell, what is wrong exactly. Note, that user hasn't access to your code, and ArgumentNullException on your method call is much more cleaner, that NullReferenceException inside of your method.
On the other hand, your release code should be clean from extra checks.
If you'll return null from any private method, and then try to access a member of that null value, you'll get NRE during debugging or testing. Then you have to fix error in your code and forget it. If you have fixed the error, there's no reason to keep the null-checking alive. This all doesn't avoid Debug.Assert/Contract.Assert or similar. But: a) those thing should live in debug version of your code; b) they must not be after every line of code, because this reduces code readability; c) often it is enough to catch an exception in debugger/unit test result.
Consider the "application" where an object (Thrower) is throwing a ball to another object (Receiver). The event (BallIsThrowed) happens when the ball is thrown.
Here are the 2 classes :
then the entry point :
And finally the methods pointed by the delegate when events are fired :
This is working well.
Now I want to comment this line :
because I want to say that the ball was not thrown.
The result is a null Exception :
This is normal because at this point BallIsThrowed is null
To solve this, I initilise my event :
But then my problem is that my code is never taking the event when I decomment "receiver.Register(thrower)"
My questions are :
How can I have the 2 method EventMethod fired ?
The best practice way to fire an event looks like this:
EventHandler ballIsThrowed = BallIsThrowed;
if (ballIsThrowed != null)
{
ballIsThrowed(this, EventArgs.Empty);
}
The reason for the temporary variable is to prevent race conditions between the null check and the execution.
Bear with me: you are setting an event handler to BallIsThrowed, then you use IthrowTheBall() to actually trigger the event, with this code:
BallIsThrowed(this, EventArgs.Empty);
When you try to call an event handler, in this case the BallIsThrowed, it must exists or it will throw a NullReferenceException. So, in order for this to work, it must exists an event handler BallIsThrowed. It actually does, until you comment this line:
//BallIsThrowed += new EventHandler(method.EventMethod2);
So basically you need to verify if the EventHandler exists before firing it:
if (BallIsThrowed != null)
BallIsThrowed(this, EventArgs.Empty);
Another (more elegant, if you ask me) way of doing this is:
(BallIsThrowed??delegate{})(this, EventArgs.Empty);
Compact, thread safe...
Two things that need to be corrected:
In your Event Invoker, check EventHandler != null because you dont know if anyone registered to your handler.
var ballThrown = BallIsThrowed;
if (ballThrown != null)
{
ballThrown(this, EventArgs.Empty);
}
For general knowledge, In order to register more then one delegate to your
EventHandler, dont register it via the = operator, but via the
+= operator, meaning you want to append a new delegate:
public void IThrowTheBall()
{
// Do stuff
// You dont have to register this delegate, you can append it to the current
// delegates already registered
BallIsThrowed += method.EventMethod1;
}
I have a ton on controls on a form, and there is a specific time when I want to stop all of my events from being handled for the time being. Usually I just do something like this if I don't want certain events handled:
private bool myOpRunning = false;
private void OpFunction()
{
myOpRunning = true;
// do stuff
myOpRunning = false;
}
private void someHandler(object sender, EventArgs e)
{
if (myOpRunning) return;
// otherwise, do things
}
But I have A LOT of handlers I need to update. Just curious if .NET has a quicker way than having to update each handler method.
You will have to create your own mechanism to do this. It's not too bad though. Consider adding another layer of abstraction. For example, a simple class called FilteredEventHandler that checks the state of myOpRunning and either calls the real event handler, or suppresses the event. The class would look something like this:
public sealed class FilteredEventHandler
{
private readonly Func<bool> supressEvent;
private readonly EventHandler realEvent;
public FilteredEventHandler(Func<bool> supressEvent, EventHandler eventToRaise)
{
this.supressEvent = supressEvent;
this.realEvent = eventToRaise;
}
//Checks the "supress" flag and either call the real event handler, or skip it
public void FakeEventHandler(object sender, EventArgs e)
{
if (!this.supressEvent())
{
this.realEvent(sender, e);
}
}
}
Then when you hook up the event, do this:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
When WhateverEvent gets raised, it will call the FilteredEventHandler.FakeEventHandler method. That method will check the flag and either call, or not call the real event handler. This is pretty much logically the same as what you're already doing, but the code that checks the myOpRunning flag is in only one place instead of sprinkled all over your code.
Edit to answer question in the comments:
Now, this example is a bit incomplete. It's a little difficult to unsubscribe from the event completely because you lose the reference to the FilteredEventHandler that's hooked up. For example, you can't do:
this.Control.WhateverEvent += new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler;
//Some other stuff. . .
this.Control.WhateverEvent -= new FilteredEventHandler(() => myOpRunning, RealEventHandler).FakeEventHandler; //Not gonna work!
because you're hooking up one delegate and unhooking a completely different one! Granted, both delegates are the FakeEventHandler method, but that's an instance method and they belong to two completely different FilteredEventHandler objects.
Somehow, you need to get a reference to the first FilteredEventHandler that you constructed in order to unhook. Something like this would work, but it involves keeping track of a bunch of FilteredEventHandler objects which is probably no better than the original problem you're trying to solve:
FilteredEventHandler filter1 = new FilteredEventHandler(() => myOpRunning, RealEventHandler);
this.Control.WhateverEvent += filter1.FakeEventHandler;
//Code that does other stuff. . .
this.Control.WhateverEvent -= filter1.FakeEventHandler;
What I would do, in this case, is to have the FilteredEventHandler.FakeEventHandler method pass its 'this' reference to the RealEventHandler. This involves changing the signature of the RealEventHandler to either take another parameter:
public void RealEventHandler(object sender, EventArgs e, FilteredEventHandler filter);
or changing it to take an EventArgs subclass that you create that holds a reference to the FilteredEventHandler. This is the better way to do it
public void RealEventHandler(object sender, FilteredEventArgs e);
//Also change the signature of the FilteredEventHandler constructor:
public FilteredEventHandler(Func<bool> supressEvent, EventHandler<FilteredEventArgs> eventToRaise)
{
//. . .
}
//Finally, change the FakeEventHandler method to call the real event and pass a reference to itself
this.realEvent(sender, new FilteredEventArgs(e, this)); //Pass the original event args + a reference to this specific FilteredEventHandler
Now the RealEventHandler that gets called can unsubscribe itself because it has a reference to the correct FilteredEventHandler object that got passed in to its parameters.
My final advice, though is to not do any of this! Neolisk nailed it in the comments. Doing something complicated like this is a sign that there's a problem with the design. It will be difficult for anybody who needs to maintain this code in the future (even you, suprisingly!) to figure out the non-standard plumbing involved.
Usually when you're subscribing to events, you do it once and forget it - especially in a GUI program.
You can do it with reflection ...
public static void UnregisterAllEvents(object objectWithEvents)
{
Type theType = objectWithEvents.GetType();
//Even though the events are public, the FieldInfo associated with them is private
foreach (System.Reflection.FieldInfo field in theType.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance))
{
//eventInfo will be null if this is a normal field and not an event.
System.Reflection.EventInfo eventInfo = theType.GetEvent(field.Name);
if (eventInfo != null)
{
MulticastDelegate multicastDelegate = field.GetValue(objectWithEvents) as MulticastDelegate;
if (multicastDelegate != null)
{
foreach (Delegate _delegate in multicastDelegate.GetInvocationList())
{
eventInfo.RemoveEventHandler(objectWithEvents, _delegate);
}
}
}
}
}
You could just disable the container where all these controls are put in. For example, if you put them in a GroupBox or Panel simply use: groupbox.Enabled = false; or panel.Enabled = false;. You could also disable the form From1.Enabled = false; and show a wait cursor. You can still copy and paste these controls in a container other than the form.
I have event handlers for intercepting call logs on windows mobile. The problem is more specific to event handlers in C# rather than windows mobile. I am able to attach and detach event handlers for the first time. The proble is: I am not able to attach event handler after its detached at least once. Why are the event handlers not invoked after its detached and again attached?
Below is my code:
private static SystemState stateIncoming = null;
private static SystemState stateOutgoing = null;
private static SystemState stateTalking = null;
public static void StartCallLogInterception()
{
if (stateIncoming == null && stateOutgoing == null && stateTalking == null)
{
stateIncoming = new SystemState(SystemProperty.PhoneIncomingCall);
stateIncoming.Changed += new ChangeEventHandler(stateIncoming_Changed);
stateOutgoing = new SystemState(SystemProperty.PhoneCallCalling);
stateOutgoing.Changed += new ChangeEventHandler(stateOutgoing_Changed);
stateTalking = new SystemState(SystemProperty.PhoneCallTalking);
stateTalking.Changed += new ChangeEventHandler(stateTalking_Changed);
}
}
public static void EndCallLogInterception()
{
if (stateIncoming != null && stateOutgoing != null && stateTalking != null)
{
stateIncoming.Changed -= new ChangeEventHandler(stateIncoming_Changed);
stateIncoming = null;
stateOutgoing.Changed -= new ChangeEventHandler(stateOutgoing_Changed);
stateOutgoing = null;
stateTalking.Changed -= new ChangeEventHandler(stateTalking_Changed);
stateTalking = null;
}
}
EDIT: I updated code to include class level variable. Also, below answers conflict with each other. If I am disposing object, I must re-create the object when I need to attach event handler. Does this make sense?
EDIT 2: The problem is not with objects or event handling code. I am using LargeIntervalTimer from OpenNETCF. Whenever I am running timer using LargeIntervalTimer, the event handler is not attached properly. Without LargeIntervalTimer, everything is working fine.
Well, it's not really clear from just the code you've given, but I wonder whether it's because you're never disposing of the SystemState objects you're creating. If you change your code to dispose of them properly when you unsubscribe, that may help.
Alternatively, don't bother keeping on creating new objects - just create the three objects up-front, and then subscribe/unsubscribe as appropriate.
You don't need the
stateTalking.Changed -= new ChangeEventHandler(stateTalking_Changed);
code. First, you are not removing the same thing you put in, you are removing a new instance og the ChangeVenetHandler. Second, all event handlers are removed when you run
stateTalking = null;
because of the Garage Collection.
As Jon Skeet said, you never run the code
stateTalking.Dispose();
before you remove it.
Check your if statements. Place a break point and make sure that you even enter the condition that wires-up the handlers. I suspect that you are not reaching the code in subsequent calls, likely because one of the objects is not null.