NullReferenceException when attempting to fire an event - c#

I'm learning about creating events and creating multi-threaded applications.
The method Thread is called by another class which populates the params with search conditions. A BackgroundWorker is created, performs a search and returns the results to worker_RunWorkerCompleted.
Within worker_RunWorkerCompleted, I want to send the results back to my UI which is subscribing to the Fireendofsearch event.
I'm having trouble understanding why my code below throws the following error
Object reference not set to an instance of an object.
when I fire the event Fireendofsearch
public class BackgroundSearch
{
public event SearchResultCompleteThreaded Fireendofsearch;
public EventArgs a = null;
public delegate void SearchResultCompleteThreaded(object seachresults, EventArgs a);
internal void Thread(string folder, string parms)
{
var Argument = new List<object> { folder, parms };
var worker = new BackgroundWorker {WorkerReportsProgress = false, WorkerSupportsCancellation = false};
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.RunWorkerAsync(Argument);
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var passedAugue = e.Argument as List<object>;
var returnResult = new List<string[]>();
if (passedAugue != null)
{
var result = Directory.GetFiles(passedAugue[0].ToString(), passedAugue[1].ToString(), SearchOption.AllDirectories);
foreach (string s in result)
{
var t = new string[4];
t[0] = s;
t[1] = File.GetCreationTime(s).ToString();
t[2] = File.GetLastAccessTime(s).ToString();
t[3] = File.GetLastWriteTime(s).ToString();
returnResult.Add(t);
}
}
if (returnResult.Count != 0)
{
e.Result = returnResult;
}
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
Fireendofsearch(e.Result, a);
}
}
}

Firendofsearch will be null until someone subscribes to it, change your work completed event handler to this to fix it.
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
var friendOfSearch = Fireendofsearch;
if(friendOfSearch != null)
friendOfSearch (e.Result, a);
}
}
The reason I copy it to a variable is if someone in another thread is the last person to unsubscribe between the null check and the raising of the event you will still get the null reference exception, by coping to another variable first it solves that problem.
However I would make some other changes if I where writing it, you are retuning a null EventArgs for some reason and passing the result back as the "Sender" in the traditional event pattern. I would change your code to this
public event EventHandler<FriendOfSearchArgs> FirendsOfSearch;
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
{
RaiseFriendOfSearch(e.Result);
}
}
protected virtual void RaiseFriendOfSearch(object result)
{
var friendOfSearch = FirendsOfSearch;
if(friendOfSearch != null)
friendOfSearch(this, new FriendOfSearchArgs(result));
}
public class FriendOfSearchArgs : EventArgs
{
public FriendOfSearchArgs(object result)
{
Result = result;
}
public object Result {get; private set;}
}
This was all written in the SO text box so there may be one or two errors.

You should check for null before trying to invoke the delegate. And you have to pull it into a separate variable first to avoid threading issues.
var ev = Fireendofsearch;
if ( ev != null ) ev( ... );
I have also found it useful to have an extension method for this case:
public static void Raise ( this EventHandler h, object sender )
{
if ( h != null) h( sender, EventArgs.Empty );
}
And then:
MyEvent.Raise ( this );

"Behind" your public event, there's an implicit private variable of type SearchResultCompleteThreaded. The type SearchResultCompleteThreaded is a delegate type.
In .NET all delegates are "multicast" delegates. That means they have an invocation list (your GetInvocationList() method on SearchResultCompleteThreaded is derived from System.Delegate.GetInvocationList().
Now, in .NET, the invocation list is guaranteed to consist of one or more items (not zero or more). Any delegate type is an immutable type. But if you try to create a new instance by "subtracting away" all members in the invocation list of an existing instance, as in var newDel = oldDel - oldDel; or reuseDel -= reuseDel;, then instead of getting a new instance with a zero-length invocation list, you get a null reference!
The good thing about this is that you don't have to worry about a subtle difference between an "empty" delegate instance (which could otherwise have been permitted) and a null reference. The bad thing about it, is the problem you had above.

For some reason (Optimization most likely), an event is only instantiated when a first handler method subscribe to it.
You must check for that in your code.
Here's how I usually declare an event:
public event SearchResultCompleteThreaded Fireendofsearch;
private void RaiseFireEndOfSearchEvent(EventArgs e)
{
if (Fireendofsearch != null)
{
Fireendofsearch(this, e);
}
}
And whenever I need to raise the event, I would just call the helper method instead.

Set:
public event SearchResultCompleteThreaded Fireendofsearch = delegate { };
Needs initialized possibly?

Related

WinForms App w/BackgroundWorker & Class Object Events

I'm working on a WinForms application that does a bunch of File Processing. This processing is done in multiple class objects that have "events" to publish different types of messages. One type is a "status" of what is being worked on and Two is an "output" that documents issues or change that were made.
I added a BackgroundWorker process and have that working fine but the only notifications I see is a ProgressPercentage. If I subscribe & raise any of the class events I get a "Cross-thread operation .. " which is understandable. What is the best way to implement this so that these components can be used by a winforms app and a non interactive process as well?
Here is what my DoWork looks like now, but the events cause the "Cross-thread":
var search = SearchToolFactory.Get(Convert.ToInt32(checkedTypeButton.Tag));
search.RaiseUpdateSearchEvent += new EventHandler<UpdateEventArgs>(search_RaiseUpdateSearchEvent);
search.RaiseUpdateOutputEvent += new EventHandler<UpdateEventArgs>(search_RaiseUpdateOutputEvent);
search.Process(Convert.ToInt32(checkedScopeButton.Tag), txtInput.Text, txtPattern.Text);
And here is the UpdateEventArgs, just a smilple class to pass a message:
public class UpdateEventArgs: EventArgs
{
public UpdateEventArgs(string s)
{
update = s;
}
private string update;
public string Update
{
get { return update; }
set { update = value; }
}
}
So based on PMF's comment I got the following to work:
void search_RaiseUpdateOutputEvent(object sender, UpdateEventArgs e)
{
if (InvokeRequired)
{
this.Invoke( (MethodInvoker)delegate{ txtOutput.Text += e.Update; });
}
}
void search_RaiseUpdateSearchEvent(object sender, UpdateEventArgs e)
{
if (InvokeRequired)
{
this.Invoke((MethodInvoker)delegate { txtSearching.Text = e.Update; });
}
}
But also see and understand the concern Hans Passant stated about structural issues and that my architecture is flawed and still needs additional work.
Thanks
dbl
BackgroundWorker.ReportProgress() has an overload with two arguments. The second can be anything you want. Like a delegate:
public event EventHandler Foo;
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
//...
backgroundWorker1.ReportProgress(0, new Action(() => {
var handler = Foo;
if (handler != null) handler(this, EventArgs.Empty);
}));
//...
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
if (e.UserState != null) ((Action)e.UserState)();
else this.progressBar1.Value = e.ProgressPercentage; // optional
}
You need to Invoke() any user-interface event from your background-worker thread. Or you invoke the actual update in the form code (the later solution is probably the nicer way because it keeps the UI logic to the UI part of the application).
That would look similar to the following (for an event handler listening to status changes):
public void OnStatusUpdated(Status newStatus)
{
if (InvokeRequired)
{
Invoke(delegate
{
statusControl.Text = newStatus.ToString(); // Or something like it.
});
}
}

Avoiding null pointer exception in delegates

I'm using delegates
in my c# windows forms application project.Using that I'm trying to remove items in a list box. I'm getting this null pointer exception and can somebody suggest a way to avoid that?
Delegate
public delegate void OrderEventDelegate (Object sender, OrderEventArgs args);
OrderEventArgs class
public class OrderEventArgs
{
private String message;
public String Message
{
get { return message; }
set { message = value; }
}
private int tableNo;
public int TableNo
{
get { return tableNo; }
set { tableNo = value; }
}
}
Class 1
public partial class Class1 : Form
{
private event OrderEventDelegate readyEvent;
public Class1(HomeForm parent, int tableNo)
{
InitializeComponent();
readyEvent -= new OrderEventDelegate(parent.readyOrder);
}
public void button_click()
{
OrderEventArgs readyOrderArg = new OrderEventArgs();
readyOrderArg.TableNo = 1;
readyOrderArg.Message = "123";
readyEvent(this, readyOrderArg);
}
}
Here readyEvent -= new OrderEventDelegate(parent.readyOrder);readyOrder() is the method which remove items in the list, which is located in the 'Homeform'.
Exception
It is possible to initialize C# events with an empty delegate. This way it can always be called safely without a null pointer check. As shown in this answer: https://stackoverflow.com/a/340618/2404788.
public delegate void OrderEventDelegate (Object sender, OrderEventArgs args) = delegate {};
If there's a possibility of something being null and you can do something about it/don't want to critically fail when it is, then check for it:
if (readyEvent != null) {
readyEvent( ... );
}
But the point here, I suppose, is that you don't want this thing to be null; so you should subscribe a handler to the event. I'm not sure why you're trying to remove a new instance of the delegate handler, but to add one you would use +=.

Pass additional parameters or objects using an Event Handler [duplicate]

This question already has answers here:
Pass extra parameters to an event handler?
(10 answers)
Closed 8 years ago.
I feel like this is really basic, but I'm having trouble with this issue. I'm using a Process object and subscribing to a DataReceivedEventHandler. This event handler then delegates to another method, in this case "DoSomething", and the signature for the arguments is (Object sender, DataReceivedEventArgs args). What I need to do, is extend something, or provide something that will pass in additional information. Here is my current code:
// an object of some type
MyCustomObject obj = new MyCustomObject();
// set up obj and Process
process.OutputDataReceived += new DataReceivedEventHandler(DoSomething);
public void DoSomething(Object sender, DataReceivedArgs args)
{
// do some stuff, however, I need the "obj" object passed in for work
}
I feel like this is really trivial, but not sure how to proceed. I've read about subclassing the "EventArgs," but not sure how that will help, or how to even change the signature of "DoSomething" to accept a DataReceivedArgsExtended parameter, since the DataReceivedEventHandler is expecting a method with a DataReceivedArgs
Yes you can extend your DataReceivedArgs to DataReceivedArgsExtended, but remeber cast it into event handler method. For example:
public class MyObject
{
public event EventHandler<MyEventArgs> OnFire;
public void Fire()
{
if( OnFire != null )
{
//var e = new MyEventArgs { X=2 };
var e = new MyEventArgsNew { X = 3, Y = 4 };
OnFire( this, e );
}
}
}
public class MyEventArgs : EventArgs
{
public int X { get; set; }
}
public class MyEventArgsNew : MyEventArgs
{
public int Y { get; set; }
}
static void Main( string[] args )
{
var obj = new MyObject();
obj.OnFire += new EventHandler<MyEventArgs>( obj_OnFire );
obj.Fire();
Console.ReadLine();
}
static void obj_OnFire( object sender, MyEventArgs e )
{
var e2 = (MyEventArgsNew)e;
Console.WriteLine( e2.X );
Console.WriteLine( e2.Y );
}
Your question is already answered in the following questions:
C# passing extra parameters to an event handler?
How can I pass addition local object variable to my event handler? [duplicate]
In your case you need to use an anonymous delegate function or a lambda expression in the same scope and from within it call your own function, containing the event handle parameters and your additional ones:
// an object of some type
MyCustomObject obj = new MyCustomObject();
// set up obj and Process
process.OutputDataReceived +=
(Object _sender, DataReceivedArgs _args) =>
DoSomething(obj, _sender, _args);
public void DoSomething(Process process, Object sender, DataReceivedArgs args)
{
// do some stuff
}

Adding an action to event handler arguments c#

I have a problem with some code I need to refactor. Right now it uses lambdas as event handlers, but they are not removed properly. From what I have read, this is not even possible? Anyway I would like to rewrite it to use a delegate instead of an anonymous function, and now my problem is that right now it takes an action as parameter, and I can't seem to figure out how to pass the action on to my new delegate. This is the code:
void RetrieveData(
int pointId,
int? chartCollectionId,
Action action)
{
if (pointId <= 0)
throw new ArgumentException("PointId not valid");
LastPointId = NextPointId;
NextPointId = pointId;
Clear();
_csr = new CustomerServiceRepository();
_csr.ServiceClient.GetChartDataCompleted += (se, ea) =>
{
_cachedCharts = ea.Result;
ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
_csr = null;
};
_csr.ServiceClient.GetChartDataAsync(
Settings.Current.Customer.CustomerName,
pointId,
chartCollectionId);
_csr.ServiceClient.GetChartDataCompleted -= (se, ea) => //remove after usage
{
_cachedCharts = ea.Result;
ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
_csr = null;
};
}
I was thinking that maybe I could create the following:
public class extendedEventArgs : GetChartDataCompletedEventArgs
{
Action foo { get; set; }
}
void tang(object sender, extendedEventArgs e)
{
_cachedCharts = e.Result;
ChartDataRetrieved(e.Result);
if (action != null)
action.Invoke();
_csr = null;
}
And the pass the action as a parameter in the extended event args, but when I try to use it like this
_csr.ServiceClient.GetChartDataCompleted += new EventHandler<extendedEventHandler>(tang);
It gives an error:
Cannot implicitly convert type System.EventHandler<Conwx.Net.Client.CustomerClient.Controls.ChartControls.ChartListForecast.extendedEventArgs>' to System.EventHandler<Conwx.Net.Client.Framework.CustomerServiceReference.GetChartDataCompletedEventArgs>'
What am I doing wrong here? Alternative solutions are also welcome.
.K
As I read it, the key problem here is not being able to remove the handler; if so, all you need it to store the delegate (where in the below, YourDelegateType is meant to mean: the defined type of GetChartDataCompleted):
YourDelegateType handler = (se, ea) =>
{
_cachedCharts = ea.Result;
ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
_csr = null;
};
_csr.ServiceClient.GetChartDataCompleted += handler;
...
_csr.ServiceClient.GetChartDataCompleted -= handler;
You can also make it self-unsubscribing (i.e. so that it unsubscribes when the event is raised):
YourDelegateType handler = null;
handler = (se, ea) =>
{
_cachedCharts = ea.Result;
ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
_csr.ServiceClient.GetChartDataCompleted -= handler;
_csr = null;
};
_csr.ServiceClient.GetChartDataCompleted += handler;
No, you can't do this because it's the class which raises the GetChartDataCompleted event which creates the object passed (as a reference) to the event handler. It will be creating a GetChartDataCompletedEventArgs - not an extendedEventArgs.
If you think about it, it's like trying to implement an interface which looks like this:
public interface IFoo
{
void Foo(object x);
}
with a class like this:
public class Bar : IFoo
{
// We don't care if someone calling IFoo wants to pass us something
// other than a string - we want a string, darn it!
public void Foo(string y)
{
Console.WriteLine(y.Length);
}
}
That's clearly not going to work...
Marc has shown one approach to fixing it - but I'd also point out that you should probably actually only be removing the delegate when the event fires. I'm assuming that the fact that the method is called GetChartDataAsync means it's a non-blocking method... so unsubscribing from the event immediately after calling it probably isn't a great idea.
If you'd prefer to avoid the anonymous methods, you can manually do essentially what the compiler is doing for you under the hood. That is, create a closure class to hold the Action and a reference to itself as fields and which exposes the method you want to assign to the event. Something like this:
class RetrieveDataClosure
{
private Action action;
private MyClass self;
public RetrieveDataClosure(Action action, MyClass self)
{
this.action = action;
this.self = self;
}
public void ChartDataCompleted(object se, MyEventArgs ea)
{
self._cachedCharts = ea.Result;
self.ChartDataRetrieved(ea.Result);
if (action != null)
action.Invoke();
self._csr = null;
}
}
Which you'd use in your code like this:
var closure = new RetrieveDataClosure(action, this);
_csr = new CustomerServiceRepository();
_csr.ServiceClient.GetChartDataCompleted += closure.ChartDataCompleted;
_csr.ServiceClient.GetChartDataAsync(
Settings.Current.Customer.CustomerName,
pointId,
chartCollectionId);
_csr.ServiceClient.GetChartDataCompleted -= closure.ChartDataCompleted;

C#: Triggering an Event when an object is added to a Queue

I need to be able to trigger a event whenever an object is added to a Queue<Delegate>.
I created a new class that extends Queue:
public delegate void ChangedEventHandler(object sender, EventArgs e);
public class QueueWithChange<Delegate> : Queue<Delegate>
{
public event ChangedEventHandler Changed;
protected virtual void OnChanged(EventArgs e) {
if (Changed != null)
{
Changed(this, e);
}
}
}
And then attached the event from another class, like such:
QueueWithChange<TimerDelegate> eventQueue = new QueueWithChange<TimerDelegate>();
//
eventQueue.Changed += new ChangedEventHandler(delegate(object s, EventArgs ex) {
//This event is not being triggered, so this code is unreachable atm...and that is my problem
if (eventQueue.Count > 0)
{
eventQueue.Dequeue().Invoke(new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(5) });
actionTimer.Stop();
}
});
But whenever I enqueue an object (eventQueue.Enqueue(something)), the attached event is not being fired.
What am I missing here?
If you mean the non-generic Queue class, then you can just override Enqueue:
public override void Enqueue(object obj)
{
base.Enqueue(obj);
OnChanged(EventArgs.Empty);
}
However, if you mean the generic Queue<T> class, then note that there is no suitable virtual method to override. You might do better to encapsulate the queue with your own class:
(** important edit: removed base-class!!! **)
class Foo<T>
{
private readonly Queue<T> queue = new Queue<T>();
public event EventHandler Changed;
protected virtual void OnChanged()
{
if (Changed != null) Changed(this, EventArgs.Empty);
}
public virtual void Enqueue(T item)
{
queue.Enqueue(item);
OnChanged();
}
public int Count { get { return queue.Count; } }
public virtual T Dequeue()
{
T item = queue.Dequeue();
OnChanged();
return item;
}
}
However, looking at your code, it seems possible that you are using multiple threads here. If that is the case, consider a threaded queue instead.
I just did write up on what I call a TriggeredQueue. It's inspired the answer by Marc Gravell.
You can find my post here: http://joesauve.com/triggeredqueuet
And the Gist here: http://gist.github.com/jsauve/b2e8496172fdabd370c4
It has four events:
WillEnqueue
WillDequeue
DidEnqueue
DidDequeue
You can hook into any of these like so:
YourQueue.WillEnqueue += (sender, e) => {
// kick off some process
};
YourQueue.DidEnqueue += (sender, e) => {
// kick off some process
// e.Item provides access to the enqueued item, if you like
};
YourQueue.WillDequeue += (sender, e) => {
// kick off some process
};
YourQueue.DidDequeue += (sender, e) => {
// kick off some process
// e.Item provides access to the dequeued item, if you like
};
One neat trick is that you can use the DidDequeue method to kick off some process to ensure that the queue is full by making a web request or loading some data from a filesystem, etc. I use this class in Xamarin mobile apps to ensure that data and images are pre-cached in order to provide a smooth user experience, instead of loading images AFTER they scroll onto the screen (like you might see in Facebook and countless other apps).
try
public new void Enqueue(Delegate d)
{
base.Enqueue(d);
OnChanged(EventArgs.Empty);
}
You have to override Enqueue, to call OnChanged.

Categories

Resources