I am trying out the ExcelAsyncUtil.Observe function. I made the following code that shows a running clock in Excel. It works fine but I am not sure what I am doing. Two questions:
Should I add functionality for observer.OnCompleted() and observer.OnError()? What does these calls do?
What should I do in the IDisposible class? Why is it there?
Here is my sample code:
[ExcelFunction]
public static object MyExcelTicker()
{
return ExcelAsyncUtil.Observe("MyExcelTicker", new object[] { }, TickerFunction());
}
public static ExcelObservableSource TickerFunction()
{
ExcelObservableSource source = new ExcelObservableSource(() => new TickerObservable());
return source;
}
public class TickerObservable : IExcelObservable
{
public IDisposable Subscribe(IExcelObserver observer)
{
var timer = new System.Timers.Timer();
timer.Interval = 1000;
timer.Elapsed += (s, e) => observer.OnNext(DateTime.Now.ToString());
timer.Start();
// What about observer.OnCompleted() and observer.OnError()?
return new TickerDisposable();
}
}
public class TickerDisposable : IDisposable
{
public void Dispose()
{
// What to do here?
}
}
It has been a while and at least one thing is still left not covered, so let me add to what Govert said.
You've asked:
public class TickerDisposable : IDisposable
{
public void Dispose()
{
// What to do here?
}
}
Let's summarize:
For each new subscriber to your clock-ticker, a Subscribe will be called on the TickerObservable. Therefore, for each subscriber, your code will create a new System.Timers.Timer and a new timer.Elapsed event handler - to get your intended effect. And this is actually all that you need to get your effect.
However, you are also required to return an IDisposable, therefore you've created a dummy TickerDisposable solely for that purpose, and you are not sure what it is for.
Answer:
The IDisposable that the library requires you to return from the Subscribe is there just to allow you to cleanup after your glitter stops shining. Timers are a "system thing". Once you create them and start them, they run. After an hour they cannot be GC'ed, because they are meant to be run until you stop them. Surely, you've +='ed an event hander, the observer (if weakly-reference'd) might be already dead, but your timer does not know! You must stop it at some point.
Hence, IDisposable-related pattern, borrowed from RX: whatever heavy or long-living you allocate, reserve, build, etc in the Subscribe method, put some note about it into that (yours!) IDisposable. Then, when the observer unsubscribes, your IDisposable will get cleaned too, and your custom Dispose method will be run, that will be able to look at your IDiposable's contents and .. cleanup the garbage, or rather, unlock it, so the GC can flush them.
Completing your example:
public class TickerObservable : IExcelObservable
{
public IDisposable Subscribe(IExcelObserver observer)
{
var timer = new System.Timers.Timer();
timer.Interval = 1000;
timer.Elapsed += (s, e) => observer.OnNext(DateTime.Now.ToString());
timer.Start();
return new TickerDisposable(timer);
}
}
public class TickerDisposable : IDisposable
{
private Timer ticky;
public TickerDisposable(Timer timer)
{
ticky = timer;
}
public void Dispose()
{
if(ticky != null)
ticky.Dispose(); // or Stop, or etc..
}
}
The above example is actually most-obvious usage of the returned IDisposable. However, you can use it for any register-unregister notification. For example, with single shared timer, it might look like this:
public class TickerObservable : IExcelObservable
{
private static Timer timer = ..... ; // assume it is up & running & shared
public IDisposable Subscribe(IExcelObserver observer)
{
ElapsedEventHander hd = (s, e) => observer.OnNext(DateTime.Now.ToString());
timer.Elapsed += hd;
return new TickerDisposable(timer, hd);
}
}
public class TickerDisposable : IDisposable
{
private Timer ticky;
private ElapsedEventHander handler;
public TickerDisposable(Timer timer, ElapsedEventHander hd)
{
ticky = timer;
handler = hd;
}
public void Dispose()
{
if(ticky != null && handler != null)
ticky.Elapsed -= handler;
}
}
And now you are perfectly sure that no dead-handlers are lingering at the long-living-shared-timer. (of course the cleanup of the timer is missing here, but that's another thing..). Probably you already got the idea, so, have fun!
The IExcelObserver interface matches the semantics of the IObserver interface from the Reactive Extensions library (http://msdn.microsoft.com/en-us/library/dd783449.aspx).
You function can call OnNext zero or more times, and then call OnError if an error occurs, or OnCompleted if no further events will be raised. Excel-DNA will handle OnError as it would an exception thrown by a regular UDF, and will return #VALUE to the cell or process the exception via the registered UnhandledExceptionHandler. OnCompleted is not so useful in the Excel context - it just indicates that no further values will be raised.
For your example, error don't seem to be a problem, and there is no end to the stream of events, so you need never call OnError or OnCompleted.
The Excel-DNA infrastructure will call the IDisposable.Dispose when the observable is no longer hooked up to a cell formula. For example, if the formula with the MyExcelTicker() call is deleted from the cell. You can use this as a notification to clean up any back-end resources, or ignore the notification if you're not interested.
Related
In .NET I've been essentially raised to never ever forget to unsubscribe to events. In MVVM apps what I often end up with is this construct.
public class WindowVm
{
public EntityModel MyModel { get; set; }
void Subscribe()
{
MyModel.PropertyChanged += DoSomething;
}
void Unsubscribe()
{
MyModel.PropertyChanged -= DoSomething;
}
}
I need to unsubscribe, because if I don't MyModel would keep a reference to the WindowVm and keep it alive for as long as MyModel lives.
I just found out about reactive extensions, but I can't find out whether I need to still think about this. I cant find anywhere that says so, but I also don't hear "oh and btw it solves that annoying event unsubscribe problem" which would be a killer argument.
public class WindowVm
{
public EntityModel MyModel { get; set; }
void Subscribe()
{
MyModel.PropChangedObservable.Subscribe(e => DoSomething(e.Sender, e.EventArgs));
}
}
public class EntityModel : ObservableBase
{
public IObservable<EventPattern<PropertyChangedEventArgs>> PropChangedObservable { get; private set; }
public EntityModel()
{
PropChangedObservable = Observable
.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
x => this.PropertyChanged += x,
x => this.PropertyChanged -= x);
}
}
I can't tell whether this would create an extra reference. And what about this way?
public class WindowVm
{
public EntityModel MyModel { get; set; }
void Subscribe()
{
Observable
.FromEventPattern<PropertyChangedEventHandler, PropertyChangedEventArgs>(
x => MyModel.PropertyChanged += x,
x => MyModel.PropertyChanged -= x)
.Subscribe(e => DoSomething(e.Sender, e.EventArgs));
}
}
I have not worked with RX but the Subscribe method that you are using is returning an IDisposable which should then be used by the consumer to unsubscribe.
Your method:
void Subscribe()
{
MyModel.PropChangedObservable.Subscribe(e => DoSomething(e.Sender, e.EventArgs));
}
Should then be:
IDisposable Subscribe()
{
return MyModel.PropChangedObservable.Subscribe(e => DoSomething(e.Sender, e.EventArgs));
}
Note: I'm guessing your code is not 100% complete as you are missing the method to be invoked on Subscribe and you have hard-coded DoSomething and I'm ignoring that part
So the observer of your model would first call subscribe and acquire the reference to IDisposable. Once the observer is finished then it should call Dispose on that reference.
To answer your question
I can't find out whether I need to still think about this
The answer is yes, you still need to think about this. However the RX does have automatic unsubscription but you need to know how many events you want to listen to. Check the answer of this SO question.
I am using this approach in WinForms and it will also work for WPF.
Unfortunately, it changes the generated code, but VS won't replace it when you change things in designer.
public MainForm()
{
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
}
Open InitializeComponent method and in designer file change dispose method.
'UserControl overrides dispose to clean up the component list.
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
OnDisposing() // <-- Add this line
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub
Implement OnDispose under the ctor (I usually place it before Load)
public void OnDisposing()
{
var context = // TODO: get VM for current frm;
if (context != null)
context.Dispose(); // call dispose on VM.
}
Your VM should then unsubscribe in on Dispose method that will be called when UC is disposed (or later :D ). That will work OK for dialogs etc.
I use a proxy class in which I wrap a Process instance, I've implemented the ErrorDataReceived event that forwards to the real Process event. But either way - even without the proxy class - I just don't understand how to use this event.
The Problem is that the static Process.Start() returns a Process instance to which I can hook up my event handlers. So basically I cannot subscribe to the event before the process has been started, but then I don't have anything to subscribe to and in my case that gives me a NullReferenceException because my wrapped Process instance is null at that point.
But can I be sure that the error hasn't already occurred between invoking and subscribing? Does anybody know a better solution to catch errors? This process class is bugging me quite a bit, there are multiple ways of doing things, som are not entirely clear to me. Also reading the ErrorOutputStream blocks the application if there are no errors...
A bit of context:
public class ProcessProxy : IProcessProxy
{
private object lockObject = new object();
public Process Process { get; private set; }
public void Start(ProcessStartInfo processStartInfo)
{
Process = Process.Start(processStartInfo);
}
public bool WaitForExit(int milliseconds)
{
return Process.WaitForExit(milliseconds);
}
public event DataReceivedEventHandler ErrorDataReceived
{
add
{
lock (lockObject)
{
Process.ErrorDataReceived += value;
}
}
remove
{
lock (lockObject)
{
Process.ErrorDataReceived -= value;
}
}
}
}
If I do s.th. like this, then this obviously is a NullReferenceException because the internal Process object is null:
AprocessProxy.ErrorDataReceived += OnError;
processProxy.Start(ProcessStartInfo);
Use new to construct a Process instance, wire up the event handler, and then start the process by calling Start method on that instance.
I'm reading values from a certain process memory. Let's say that I fetch them in the following way:
var foo = memoryService.GetFoo();
var bar = memoryService.GetBar();
Since it doesn't exist any events for memory changes, I would like to create custom events using polling (if you don't have any other suggestions).
Since I don't know when the values might change, the polling interval has to be set to a suitable value. I don't know how to actually write this, but something like this might do (not sure if it compiles):
public class MemoryChange : INotifyPropertyChanged
{
private Timer _timer;
public SomethingChanged(double polingInterval)
{
_timer = new Timer();
_timer.AutoReset = false;
_timer.Interval = polingInterval;
_timer.Elapsed += timer_Elapsed;
_timer.Start();
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
}
}
Do I need to create one class that implements INotifyPropertyChanged for each value (foo and bar in this case)?
Is there any way to make it run on a non blocking thread (using a Task perhaps?)?
Do I have to use polling to make this work?
Any input is much appreciated!
If you have access to your MemoryService from your main view model, then you could define a simple delegate to solve your problem.
In your MemoryService, define the delegate and related property:
public delegate void DataUpdate(object someData);
public DataUpdate OnDataUpdate { get; set; }
In the parent view model, attach a handler for the delegate:
MemoryService memoryService = new MemoryService();
memoryService.OnDataUpdate += MemoryService_OnDataUpdate;
Back in MemoryService when the data is ready:
var foo = memoryService.GetFoo();
// Always check for null
if (OnDataUpdate != null) OnDataUpdate(foo);
Now in the parent view model:
public void MemoryService_OnDataUpdate(object someData)
{
// Do something with the new data here
}
You can find out more about using delegate objects from the Delegates (C# Programming Guide) page on MSDN.
I am not sure in what context you will be using your memory service though I will give it a try to answer your quesiton.
Yes, you will have to implement INotifyPropertyChanged in every class.
Yes there is a way, Google knows it.
You can use polling or you could listen to PropertyChanged event. That would be the callback approach where you get notified when a changes happened.
I'm trying to wrap my head around this problem. The below code ultimately merely counts the number of garbage collected objects of type SubscribersClass. When the code runs as shown, I get a SubscribersClass.Count value of 0. When I comment out the first line in EventsClass and uncomment the remainder of that class, the value of SubscribersClass.Count is 10.
The only thing I can come up with is that because there is something wrong with the EventsClass EventHandler (as shown), then no instances of SubscribersClass are actually being created.
Was hoping someone could help me to understand what is happening exactly.
This is an academic and of no practical value. Just tying to figure it out but have thus far only managed to get GoogleBlisters.
namespace understandingEvents
{
public class EventsClass
{
public event EventHandler SomeEvent; // if this is commented out and
// remainder of class is uncommented
// it works fine
/*
public event EventHandler SomeEvent
{
add
{
Console.WriteLine("An event has been added");
}
remove
{
Console.WriteLine("An event has been removed");
}
}
*/
}
public class SubscribersClass
{
static int count = 0;
static public int Count
{
get { return count; }
}
public SubscribersClass (EventsClass eventPublisher)
{
eventPublisher.SomeEvent += new EventHandler(Subscriber_SomeEvent);
}
~SubscribersClass()
{
Interlocked.Increment(ref count);
}
public void Subscriber_SomeEvent(object sender, EventArgs e)
{
Console.WriteLine("This is an event");
}
}
class Program
{
static void Main(string[] args)
{
EventsClass publisher = new EventsClass();
for (int i = 0; i < 10; i++)
{
SubscribersClass subscriber = new SubscribersClass(publisher);
subscriber = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(SubscribersClass.Count.ToString());
}
}
}
When you use a standard, compiler implemented event (public event EventHandler SomeEvent;), subscribing to the event causes a reference to the subscriber to be kept in the event, since the delegate refers to an instance method of the SubscribersClass.
This means that publisher is still holding references to each and every subscriber, so they are never released.
When you write your own add and remove handlers, you're not actually using the delegate, so the delegate is ignored (you'll find that raising the event has no effect and isn't handled by the subscribers in that case), which also means that those references aren't held, so the GC.Collect call can "clean up" the subscribers. This causes the count to increment.
When you subscribe to an event, the publisher will hold a reference to the subscriber. This is what allows the publisher to call all the methods that were subscribed to the event when the event takes place.
To solve your issue, simply remove the subscription before destroying the object. ( You'll need to store a reference to the publisher of the event). You can do this by manually calling some Unsubscribe method or (better) implement IDisposable and unsubscribe in Dispose().
public SubscribersClass (EventsClass eventPublisher)
{
m_eventPublisher = eventPublisher;
eventPublisher.SomeEvent += new EventHandler(Subscriber_SomeEvent);
}
public override void Dispose()
{
m_eventPublisher.SomeEvent -= Subscriber_SomeEvent;
}
You are not removing event susbscribers in your test code, as result the SubscribersClass instances are not getting collected.
Commented out code does not at all add listeners, so all instances of SubscribersClass are ready for GC as soon as they are created.
Note that code (when properly fixed) will also behave differently in DEBUG build due to extended lifetime of all variables. Consider putting all interesting code in a function and triggering GC outside it.
If I create a .NET class which subscribes to an event with an anonymous function like this:
void MyMethod()
{
Application.Current.Deactivated += (s,e) => { ChangeAppearance(); };
}
Will this event handler be a root keeping my class from being garbage collected?
If not, wohoo! But if so, can you show me the removal syntax? Just using -= with the same code seems wrong.
You can either use a "real" method as Simon D. suggests, or do this:
EventHandler handler = null;
handler = (s,e) => {
Application.Current.Deactivated -= handler;
ChangeAppearance();
};
Application.Current.Deactivated += handler;
Since this is somewhat ugly and defeats the purpose of using a lambda to be succint, I 'd probably go with refactoring it out to a method. But it's useful to know what else works.
Warning: It goes without saying that you have to be super ultra careful to not mess with the value of handler at all between the point where you subscribe to the event and the point where it is actually invoked, otherwise the unsubscribe part won't work correctly.
I think you do need to unsubscribe, because the event provider (the application) lives - or at least can live - longer than your consumer. So each subscribing instance dying while the app remains living will create memory leaks.
You are subscribing an anonymous delegate to the event.
This is why you can't unsubscribe it the same way, because you cannot address it anymore.
Actually you are creating the method at the same moment you subscribe, and you don't store any pointer to the newly created method.
If you slightly change your implementation to use a "real" method, you can easily unsubscribe from the event the same way you subscribe to it:
Application.Current.Deactivated += ChangeAppearance;
Application.Current.Deactivated -= ChangeAppearance;
private void ChangeAppearance(object sender, EventArgs eventArgs)
{
throw new NotImplementedException();
}
You definitely have to clean up the reference. If there were any doubt you could easily test with your own static event like so.
static class MemoryLeak
{
static List<Action<int>> list = new List<Action<int>>();
public static event Action<int> ActivateLeak
{
add
{
list.Add(value);
}
remove
{
list.Remove(value);
}
}
}
Then by setting a breakpoint in the remove function you can see that your reference is not cleaned up.
class Program
{
static void Main(string[] args)
{
foo f = new foo();
MemoryLeak.ActivateLeak += o => f.bar();
f.tryCleanup();
}
}
class foo
{
public void bar()
{ }
public void tryCleanup()
{
MemoryLeak.ActivateLeak -= o => bar();
}
}
As an alternative to Simon's solution you could use a second closure to create a "detach" action which can be passed around.
foo f = new foo();
Action<int> callfoo = o => f.bar();
MemoryLeak.ActivateLeak += callfoo;
Action cleanUp = () => MemoryLeak.ActivateLeak -= callfoo;
// Now you can pass around the cleanUp action and call it when you need to unsubscribe from the event.
cleanUp();
This is indeed a leak that prevents garbage collection. There are ways around it - with WeakReference being one of the better ones.
This link has a good conversation and good answers for you.