Summary
I have a simple class that exposes a property of type ReadOnlyObservableCollection.
When this property is accessed for the first time, quite a bit of data is generated.
The generated data is time dependent and so will change over time - hence the need for the property to be of type ReadOnlyObservableCollection.
This is working as expected, however the generated dataset is large enough that over the lifetime of the application it is guaranteed I will run into memory issues.
Problem
I need to be able to delete the references to the generated data so it can be collected by the GC. The thing is though, I cannot do this unless I know that there are no listeners on the ReadOnlyObservableCollection - or more precisely the underlying INotifyCollectionChanged.CollectionChanged event.
Is there a way I can easily be notified when a listener unsubscribes from an event without implementing said event?
One way around it which would be rather simple but perhaps a bit hacky is to keep track of all Storage objects that have active data and every so often call the CleanDataSet method if there are no listeners to the INotifyCollectionChanged.CollectionChanged event.
Sample Code
public class Storage
{
//The regular computation of the data set is not shown as it is not relevant
//to the issue at hand.
public ReadOnlyObservableCollection<String> Data
{
get
{
if (DataList == null)
{
DataList = new ObservableCollection<String>();
DataReadOnly = new ReadOnlyObservableCollection<String>(DataList);
ComputeDataSet();
//I require a way of 'listening' when the
//DataList.CollectionChanged gains or loses a subscriber.
}
return DataReadOnly;
}
}
private ReadOnlyObservableCollection<String> DataReadOnly;
private ObservableCollection<String> DataList;
private void ComputeDataSet()
{
Random random = new Random();
for (Int32 counter = 0; counter < 10000; counter++)
DataList.Add(random.Next().ToString());
}
private void CleanDataSet()
{
DataReadOnly = null;
DataList = null;
}
}
You can use the fact that ReadOnlyObservableCollection.CollectionChanged is virtual, and an event returns null if there are no subscribers:
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
public class Program
{
public static void Main()
{
var innerCollection = new ObservableCollection<string>() { "foo", "bar" };
var collection = (INotifyCollectionChanged)new MyReadOnlyObservableCollection<string>(innerCollection);
collection.CollectionChanged += Handler;
innerCollection.Add("baz");
collection.CollectionChanged -= Handler;
}
private static void Handler(object sender, NotifyCollectionChangedEventArgs e)
{
Console.WriteLine(e.NewItems[0]);
}
}
public class MyReadOnlyObservableCollection<T> : ReadOnlyObservableCollection<T>
{
public MyReadOnlyObservableCollection(ObservableCollection<T> list) : base(list) { }
private event NotifyCollectionChangedEventHandler InnerEvent;
protected override event NotifyCollectionChangedEventHandler CollectionChanged
{
add
{
if (InnerEvent == null)
{
Console.WriteLine("Got our first subscriber");
}
InnerEvent += value;
}
remove
{
InnerEvent -= value;
if (InnerEvent == null)
{
Console.WriteLine("There are no more subscribers");
}
}
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
InnerEvent?.Invoke(this, args);
}
}
We override the CollectionChanged event, and provide our own add and remove handlers. These handlers delegate to an inner event (we could just have used a delegate here, but an event's += is thread-safe, whereas a delegate's is not). In the remove handler, we also test to see whether the inner event is null: if it is, there are no more subscribers.
We also need to override OnCollectionChanged to call our inner event.
At this point we've almost rewritten ReadOnlyObservableCollection, however: it's not a big class.
Let me try to explain the design pattern I'm looking to implement. I have a Queue<Item> or some other collection of Items that needs to be processed by some method
static void Process(Item item)
{
// ...
}
They can be processed synchronously or asynchronously.
This queue will be getting items added to it periodically, because I have some method like
static void AddWorkToQueue()
{
// ...
}
that gets run on a timer
timer.Elapsed += AddWorkToQueue();
So what I need is some type "continuous while loop" that stops whenever the queue is empty.
How can I use C#.NET to do this in the best way? Are there any built-in libraries for solving this type of problem?
You could create a custom queue class and change it's listener:
public class CustomQueue : Queue<object>
{
public event EventHandler FirstItemInserted;
protected virtual void OnFirstItemInserted()
{
FirstItemInserted?.Invoke(this, EventArgs.Empty);
}
//Modified Enqueue method.
public new void Enqueue(object obj)
{
//Call the listener every time an item is inserted into the empty queue.
if (Count == 0)
{
base.Enqueue(obj);
OnFirstItemInserted();
}
else
base.Enqueue(obj);
}
}
The "Enqueue" method was changed to call the listener after each first insert.
All you'll need to do is to call a method to consume each item of your queue as long as there are items to dequeue.
class MyProgram
{
private static CustomQueue MyQueue;
public MyProgram()
{
MyQueue = new CustomQueue();
MyQueue.FirstItemInserted += ConsumeQueue;
//Activate timer...
}
private static void ConsumeQueue(object sender, EventArgs e)
{
object item;
while (MyQueue.Count > 0)
{
item = MyQueue.Dequeue();
//Do something...
}
}
}
You can use BufferBlock<Item> from TPL DataFlow (https://msdn.microsoft.com/en-us/library/hh160414(v=vs.110).aspx):
using System.Threading.Tasks.Dataflow;
static void AddWorkToQueue()
{
queue.Send(new Item());
}
static async void MainLoop()
{
while (true) // may be you need some cancellation token to trigger end of processing
{
var item = await queue.ReceiveAsync();
ProcessItem(item);
}
}
I have extended the System.Collections.Concurrent.ConcurrentQueue to raise events as objects are enqueued. I now need to be able to use System.Management.Automation.WriteObject/WriteVerbose/WriteDebug methods to write the objects from the aforementioned events. However, I receive the following error when attempting to use System.Management.Automation.WriteObject/WriteVerbose/WriteDebug in the event handler.
Does anyone know how I can marshal the events back to the main thread so that I can use the System.Management.Automation.WriteObject/WriteVerbose/WriteDebug methods?
Here is my extended ConcurrentQueue class.
public class ConcurrentQueueEx<T> : ConcurrentQueue<T>
{
#region Private Variables
private Guid _id;
#endregion
#region Public Accessors
public Guid Id
{
get { return this._id; }
}
#endregion
#region Event Declarations
public event PSObjectAddedEventHandler PSObjectAdded;
protected virtual void OnPSObjectAdded(Guid parentId, object obj)
{
PSObjectAddedEventHandler handler = PSObjectAdded;
if (handler != null)
{
PSObjectAddedEventArgs oae = new PSObjectAddedEventArgs();
oae._ParentId = parentId;
oae._Object = obj;
handler(oae);
}
}
#endregion
#region Public Functions
public new virtual void Enqueue(T item)
{
base.Enqueue(item);
OnPSObjectAdded(this._id, item);
}
public virtual void Push(T item)
{
base.Enqueue(item);
OnPSObjectAdded(this._id, item);
}
public virtual T Pop()
{
T obj;
base.TryDequeue(out obj);
return obj;
}
#endregion
}
Here are the relevant sections from the cmdlet.
protected override void BeginProcessing()
{
base.BeginProcessing();
_messageQueue = new ConcurrentQueueEx<object>();
_messageQueue.PSObjectAdded += _messageQueue_PSObjectAdded;
_resultQueue = new ConcurrentQueueEx<object>();
_resultQueue.PSObjectAdded += _resultQueue_PSObjectAdded;
}
private void _resultQueue_PSObjectAdded(PSObjectAddedEventArgs e)
{
WriteObject(e._Object);
}
private void _messageQueue_PSObjectAdded(PSObjectAddedEventArgs e)
{
WriteVerbose(e._Object.ToString());
}
Here are the exception details.
System.Management.Automation.PSInvalidOperationException was unhandled by user code
HResult=-2146233079
Message=The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. Validate that the cmdlet makes these calls correctly, or contact Microsoft Customer Support Services.
Source=System.Management.Automation
What is the "main thread" doing while the thread on which the events are raised is queued?
If the main thread is blocked then you could make it wait on a synchronization object and then have it dequeue the objects.
If the thread is off doing something else, then you need to need it to either get it interrupted (by, say an event) or else have it poll the queue. I assume you'll want to do the first. In which case you'll need to register for an event in your cmdlet and fire the event from the other thread. The second answer here shows how to do this.
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.
});
}
}
I am currently having a hardtime understanding and implementing events in C# using delagates. I am used to the Java way of doing things:
Define an interface for a listener type which would contain a number of method definitions
Define adapter class for that interface to make things easier if I'm not interested in all the events defined in a listener
Define Add, Remove and Get[] methods in the class which raises the events
Define protected fire methods to do the dirty work of looping through the list of added listeners and calling the correct method
This I understand (and like!) - I know I could do this exactly the same in c#, but it seems that a new (better?) system is in place for c#. After reading countless tutorials explaining the use of delegates and events in c# I still am no closer to really understanding what is going on :S
In short, for the following methods how would I implement the event system in c#:
void computerStarted(Computer computer);
void computerStopped(Computer computer);
void computerReset(Computer computer);
void computerError(Computer computer, Exception error);
^ The above methods are taken from a Java application I once made which I'm trying to port over to c#.
Many many thanks!
You'd create four events, and methods to raise them, along with a new EventArgs-based class to indicate the error:
public class ExceptionEventArgs : EventArgs
{
private readonly Exception error;
public ExceptionEventArgs(Exception error)
{
this.error = error;
}
public Error
{
get { return error; }
}
}
public class Computer
{
public event EventHandler Started = delegate{};
public event EventHandler Stopped = delegate{};
public event EventHandler Reset = delegate{};
public event EventHandler<ExceptionEventArgs> Error = delegate{};
protected void OnStarted()
{
Started(this, EventArgs.Empty);
}
protected void OnStopped()
{
Stopped(this, EventArgs.Empty);
}
protected void OnReset()
{
Reset(this, EventArgs.Empty);
}
protected void OnError(Exception e)
{
Error(this, new ExceptionEventArgs(e));
}
}
Classes would then subscribe to the event using either a method or a an anonymous function:
someComputer.Started += StartEventHandler; // A method
someComputer.Stopped += delegate(object o, EventArgs e)
{
Console.WriteLine("{0} has started", o);
};
someComputer.Reset += (o, e) => Console.WriteLine("{0} has been reset");
A few things to note about the above:
The OnXXX methods are protected so that derived classes can raise the events. This isn't always necessary - do it as you see fit.
The delegate{} piece on each event declaration is just a trick to avoid having to do a null check. It's subscribing a no-op event handler to each event
The event declarations are field-like events. What's actually being created is both a variable and an event. Inside the class you see the variable; outside the class you see the event.
See my events/delegates article for much more detail on events.
You'll have to define a single delegate for that
public delegate void ComputerEvent(object sender, ComputerEventArgs e);
ComputerEventArgs would be defined like this:
public class ComputerEventArgs : EventArgs
{
// TODO wrap in properties
public Computer computer;
public Exception error;
public ComputerEventArgs(Computer aComputer, Exception anError)
{
computer = aComputer;
error = anError;
}
public ComputerEventArgs(Computer aComputer) : this(aComputer, null)
{
}
}
The class that fires the events would have these:
public YourClass
{
...
public event ComputerEvent ComputerStarted;
public event ComputerEvent ComputerStopped;
public event ComputerEvent ComputerReset;
public event ComputerEvent ComputerError;
...
}
This is how you assign handlers to the events:
YourClass obj = new YourClass();
obj.ComputerStarted += new ComputerEvent(your_computer_started_handler);
Your handler is:
private void ComputerStartedEventHandler(object sender, ComputerEventArgs e)
{
// do your thing.
}
The main difference is that in C# the events are not interface-based. Instead, the event publisher declares the delegate which you can think of as a function pointer (although not exactly the same :-)). The subscriber then implements the event prototype as a regular method and adds a new instance of the delegate to the event handler chain of the publisher. Read more about delegates and events.
You can also read short comparison of C# vs. Java events here.
First of all, there is a standard method signature in .Net that is typically used for events. The languages allow any sort of method signature at all to be used for events, and there are some experts who believe the convention is flawed (I mostly agree), but it is what it is and I will follow it for this example.
Create a class that will contain the event’s parameters (derived from EventArgs).
public class ComputerEventArgs : EventArgs
{
Computer computer;
// constructor, properties, etc.
}
Create a public event on the class that is to fire the event.
class ComputerEventGenerator // I picked a terrible name BTW.
{
public event EventHandler<ComputerEventArgs> ComputerStarted;
public event EventHandler<ComputerEventArgs> ComputerStopped;
public event EventHandler<ComputerEventArgs> ComputerReset;
...
}
Call the events.
class ComputerEventGenerator
{
...
private void OnComputerStarted(Computer computer)
{
EventHandler<ComputerEventArgs> temp = ComputerStarted;
if (temp != null) temp(this, new ComputerEventArgs(computer)); // replace "this" with null if the event is static
}
}
Attach a handler for the event.
void OnLoad()
{
ComputerEventGenerator computerEventGenerator = new ComputerEventGenerator();
computerEventGenerator.ComputerStarted += new EventHandler<ComputerEventArgs>(ComputerEventGenerator_ComputerStarted);
}
Create the handler you just attached (mostly by pressing the Tab key in VS).
private void ComputerEventGenerator_ComputerStarted(object sender, ComputerEventArgs args)
{
if (args.Computer.Name == "HAL9000")
ShutItDownNow(args.Computer);
}
Don't forget to detach the handler when you're done. (Forgetting to do this is the biggest source of memory leaks in C#!)
void OnClose()
{
ComputerEventGenerator.ComputerStarted -= ComputerEventGenerator_ComputerStarted;
}
And that's it!
EDIT: I honestly can't figure out why my numbered points all appear as "1." I hate computers.
there are several ways to do what you want. The most direct way would be to define delegates for each event in the hosting class, e.g.
public delegate void ComputerStartedDelegate(Computer computer);
protected event ComputerStartedDelegate ComputerStarted;
public void OnComputerStarted(Computer computer)
{
if (ComputerStarted != null)
{
ComputerStarted.Invoke(computer);
}
}
protected void someMethod()
{
//...
computer.Started = true; //or whatever
OnComputerStarted(computer);
//...
}
any object may 'listen' for this event simply by:
Computer comp = new Computer();
comp.ComputerStarted += new ComputerStartedDelegate(
this.ComputerStartedHandler);
protected void ComputerStartedHandler(Computer computer)
{
//do something
}
The 'recommended standard way' of doing this would be to define a subclass of EventArgs to hold the Computer (and old/new state and exception) value(s), reducing 4 delegates to one. In this case that would be a cleaner solution, esp. with an Enum for the computer states in case of later expansion. But the basic technique remains the same:
the delegate defines the signature/interface for the event handler/listener
the event data member is a list of 'listeners'
listeners are removed using the -= syntax instead of +=
In c# events are delegates. They behave in a similar way to a function pointer in C/C++ but are actual classes derived from System.Delegate.
In this case, create a custom EventArgs class to pass the Computer object.
public class ComputerEventArgs : EventArgs
{
private Computer _computer;
public ComputerEventArgs(Computer computer) {
_computer = computer;
}
public Computer Computer { get { return _computer; } }
}
Then expose the events from the producer:
public class ComputerEventProducer
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerEventArgs> Error;
/*
// Invokes the Started event */
private void OnStarted(Computer computer) {
if( Started != null ) {
Started(this, new ComputerEventArgs(computer));
}
}
// Add OnStopped, OnReset and OnError
}
The consumer of the events then binds a handler function to each event on the consumer.
public class ComputerEventConsumer
{
public void ComputerEventConsumer(ComputerEventProducer producer) {
producer.Started += new EventHandler<ComputerEventArgs>(ComputerStarted);
// Add other event handlers
}
private void ComputerStarted(object sender, ComputerEventArgs e) {
}
}
When the ComputerEventProducer calls OnStarted the Started event is invoked which in turn will call the ComputerEventConsumer.ComputerStarted method.
The delegate declares a function signature, and when it's used as an event on a class it also acts as a collection of enlisted call targets. The += and -= syntax on an event is used to adding a target to the list.
Given the following delegates used as events:
// arguments for events
public class ComputerEventArgs : EventArgs
{
public Computer Computer { get; set; }
}
public class ComputerErrorEventArgs : ComputerEventArgs
{
public Exception Error { get; set; }
}
// delegates for events
public delegate void ComputerEventHandler(object sender, ComputerEventArgs e);
public delegate void ComputerErrorEventHandler(object sender, ComputerErrorEventArgs e);
// component that raises events
public class Thing
{
public event ComputerEventHandler Started;
public event ComputerEventHandler Stopped;
public event ComputerEventHandler Reset;
public event ComputerErrorEventHandler Error;
}
You would subscribe to those events with the following:
class Program
{
static void Main(string[] args)
{
var thing = new Thing();
thing.Started += thing_Started;
}
static void thing_Started(object sender, ComputerEventArgs e)
{
throw new NotImplementedException();
}
}
Although the arguments could be anything, the object sender and EventArgs e is a convention that's used very consistently. The += thing_started will first create an instance of the delegate pointing to target method, then add it to the event.
On the component itself you would typically add methods to fire the events:
public class Thing
{
public event ComputerEventHandler Started;
public void OnStarted(Computer computer)
{
if (Started != null)
Started(this, new ComputerEventArgs {Computer = computer});
}
}
You must test for null in case no delegates have been added to the event. When you make the method call however all delegates which have been added will be called. This is why for events the return type is void - there is no single return value - so to feed back information you would have properties on the EventArgs which the event handlers would alter.
Another refinement would be to use the generic EventHandler delegate rather than declaring a concrete delegate for each type of args.
public class Thing
{
public event EventHandler<ComputerEventArgs> Started;
public event EventHandler<ComputerEventArgs> Stopped;
public event EventHandler<ComputerEventArgs> Reset;
public event EventHandler<ComputerErrorEventArgs> Error;
}
Thank you all so much for your answers! Finally I'm starting to understand what is going on. Just one thing; It seems that if each event had a different number/type of arguments I'd need to create a different :: EventArgs class to deal with it:
public void computerStarted(Computer computer);
public void computerStopped(Computer computer);
public void computerReset(Computer computer);
public void breakPointHit(Computer computer, int breakpoint);
public void computerError(Computer computer, Exception exception);
This would require three classses to deal with the events!? (Well two custom, and one using the default EventArgs.Empty class)
Cheers!
Ok, FINAL clarification!: So this is pretty much the best I can do code-wise to implement those events?
public class Computer {
public event EventHandler Started;
public event EventHandler Stopped;
public event EventHandler Reset;
public event EventHandler<BreakPointEvent> BreakPointHit;
public event EventHandler<ExceptionEvent> Error;
public Computer() {
Started = delegate { };
Stopped = delegate { };
Reset = delegate { };
BreakPointHit = delegate { };
Error = delegate { };
}
protected void OnStarted() {
Started(this, EventArgs.Empty);
}
protected void OnStopped() {
Stopped(this, EventArgs.Empty);
}
protected void OnReset() {
Reset(this, EventArgs.Empty);
}
protected void OnBreakPointHit(int breakPoint) {
BreakPointHit(this, new BreakPointEvent(breakPoint));
}
protected void OnError(System.Exception exception) {
Error(this, new ExceptionEvent(exception));
}
}
}