The Reactive Extensions allow you to easily subscribe to an event using Observable.FromEventPattern, but I can't find anything on how you might implement an event when you have an IObservable.
My situation is this: I need to implement an interface which contains an event. That event is supposed to be called whenever a certain value of my object changes, and for thread safety reasons I need to call this event on a certain SynchronizationContext. I am also supposed to call each event handler with the current value on registration.
public interface IFooWatcher
{
event FooChangedHandler FooChanged;
}
Getting an observable that does what I want is rather easy with Rx using BehaviorSubject:
public class FooWatcher
{
private readonly BehaviorSubject<Foo> m_subject;
private readonly IObservable<Foo> m_observable;
public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue)
{
m_subject = new BehaviorSubject<Foo>(initialValue);
m_observable = m_subject
.DistinctUntilChanged()
.ObserveOn(synchronizationContext);
}
public event FooChangedHandler FooChanged
{
add { /* ??? */ }
remove { /* ??? */ }
}
}
Now I am looking for an easy way to have the add and remove functions subscribe and unsubscribe the passed FooChangedHandler as an Observer<Foo> on m_observable. My current implementation looks similar to this:
add
{
lock (m_lock)
{
IDisposable disp = m_observable.Subscribe(value);
m_registeredObservers.Add(
new KeyValuePair<FooChangedHandler, IDisposable>(
value, disp));
}
}
remove
{
lock (m_lock)
{
KeyValuePair<FooChangedHandler, IDisposable> observerDisposable =
m_registeredObservers
.First(pair => object.Equals(pair.Key, value));
m_registeredObservers.Remove(observerDisposable);
observerDisposable.Value.Dispose();
}
}
However, I hope to find an easier solution, because I need to implement several of these events (of differing handler types). I tried to roll my own generic solution but it creates some additional problems that need to be worked around (in particular, how you generically work with a delegate that takes a parameter of T), so I would prefer to find an existing solution that bridges the gap in this direction - just as FromEventPattern does the reverse.
You could do this:
public event FooChangedHandler FooChanged
{
add { m_observable.ToEvent().OnNext += value; }
remove { m_observable.ToEvent().OnNext -= value; }
}
However, on the remove, I think perhaps you just may want to dispose of the subscription ... or perhaps get the Action from ToEvent() and store that as a member. Untested.
EDIT: You'll have to use Action instead of a FooChangedHandler delegate, however.
EDIT 2: Here's a tested version. I suppose you need to use FooChangedHandler, however, since you have a bunch of these pre-existing handlers?
void Main()
{
IObservable<Foo> foos = new [] { new Foo { X = 1 }, new Foo { X = 2 } }.ToObservable();
var watcher = new FooWatcher(SynchronizationContext.Current, new Foo { X = 12 });
watcher.FooChanged += o => o.X.Dump();
foos.Subscribe(watcher.Subject.OnNext);
}
// Define other methods and classes here
//public delegate void FooChangedHandler(Foo foo);
public interface IFooWatcher
{
event Action<Foo> FooChanged;
}
public class Foo {
public int X { get; set; }
}
public class FooWatcher
{
private readonly BehaviorSubject<Foo> m_subject;
public BehaviorSubject<Foo> Subject { get { return m_subject; } }
private readonly IObservable<Foo> m_observable;
public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue)
{
m_subject = new BehaviorSubject<Foo>(initialValue);
m_observable = m_subject
.DistinctUntilChanged();
}
public event Action<Foo> FooChanged
{
add { m_observable.ToEvent().OnNext += value; }
remove { m_observable.ToEvent().OnNext -= value; }
}
}
Given that you are already mixing the boundaries between reactive and more normal code, you could do a less reactive version. To start simply declare a normal event pattern
public event FooChangedHandler FooChanged;
protected void OnFooChanged(Foo)
{
var temp = FooChanged;
if (temp != null)
{
temp(new FooChangedEventArgs(Foo));
}
}
and then simply connect the observable to it in the constructor
m_Observable.Subscribe(foo => OnFooChanged(foo));
It's not very Rx but it is incredibly simple.
Related
Here's the simplified case. I have a class that stores a delegate that it will call on completion:
public class Animation
{
public delegate void AnimationEnd();
public event AnimationEnd OnEnd;
}
I have another utility class that I want to subscribe to various delegates. On construction I want itself to register to the delegate, but other than that it doesn't care about the type. The thing is, I don't know how to express that in the type system. Here's my pseudo-C#
public class WaitForDelegate
{
public delegateFired = false;
// How to express the generic type here?
public WaitForDelegate<F that's a delegate>(F trigger)
{
trigger += () => { delegateFired = true; };
}
}
Thanks in advance!
Thanks to Alberto Monteiro, I just use System.Action as the type for the event. My question now is, how to pass the event to the constructor so it can register itself? This might be a very dumb question.
public class Example
{
Animation animation; // assume initialized
public void example()
{
// Here I can't pass the delegate, and get an error like
// "The event can only appear on the left hand side of += or -="
WaitForDelegate waiter = new WaitForDelegate(animation.OnEnd);
}
}
I'm afraid you can't do what you're asking.
First up, you can't constrain by delegates. The closest code to legal C# is this:
public class WaitForDelegate<F> where F : System.Delegate
{
public bool delegateFired = false;
public WaitForDelegate(F trigger)
{
trigger += () => { delegateFired = true; };
}
}
But it won't compile.
But the bigger problem is that you can't pass delegates around like this anyway.
Consider this simplified class:
public class WaitForDelegate
{
public WaitForDelegate(Action trigger)
{
trigger += () => { Console.WriteLine("trigger"); };
}
}
I then try to use it like this:
Action bar = () => Console.WriteLine("bar");
var wfd = new WaitForDelegate(bar);
bar();
The only output from this is:
bar
The word trigger doesn't appear. This is because delegates are copied by value so that the line trigger += () => { Console.WriteLine("trigger"); }; is only attaching the handler to trigger and not bar at all.
The way that you can make all of this work is to stop using events and use Microsoft's Reactive Extensions (NuGet "Rx-Main") which allows you to turn events into LINQ-based IObservable<T> instances that can get passed around.
Here's how my example code above would then work:
public class WaitForDelegate
{
public WaitForDelegate(IObservable<Unit> trigger)
{
trigger.Subscribe(_ => { Console.WriteLine("trigger"); });
}
}
And you now call it like:
Action bar = () => Console.WriteLine("bar");
var wfd = new WaitForDelegate(Observable.FromEvent(h => bar += h, h => bar -= h));
bar();
This now produces the output:
bar
trigger
Notice that the Observable.FromEvent call contains the code to attach and detach the handler in a scope that has access to do so. It allows the final subscription call to be unattached with a call to .Dispose().
I've made this class quite simple, but a more complete version would be this:
public class WaitForDelegate : IDisposable
{
private IDisposable _subscription;
public WaitForDelegate(IObservable<Unit> trigger)
{
_subscription = trigger.Subscribe(_ => { Console.WriteLine("trigger"); });
}
public void Dispose()
{
_subscription.Dispose();
}
}
An alternative if you don't want to go for the full use of Rx is to do this:
public class WaitForDelegate : IDisposable
{
private Action _detach;
public WaitForDelegate(Action<Action> add, Action<Action> remove)
{
Action handler = () => Console.WriteLine("trigger");
_detach = () => remove(handler);
add(handler);
}
public void Dispose()
{
if (_detach != null)
{
_detach();
_detach = null;
}
}
}
You call it like this:
Action bar = () => Console.WriteLine("bar");
var wfd = new WaitForDelegate(h => bar += h, h => bar -= h);
bar();
That still does the correct output.
In .NET there is already a delegate that doesn't receive no parameters, it is the Action
So you Animation class could be like that:
public class Animation
{
public event Action OnEnd;
}
But you can pass events as parameters, if you try that you will receive this compilation error
The event can only appear on the left hand side of += or -="
So lets create a interface, and declare the event there
public interface IAnimation
{
event Action OnEnd;
}
Using the interface approach you have no external dependencies and you can have many classes that implements that, also is a good practice, depends of abstractions instead concrete types. There is acronym called SOLID that explain 5 principles about better OO code.
And then your animation class implements that
Obs.: The CallEnd method is just for test purpose
public class Animation : IAnimation
{
public event Action OnEnd;
public void CallEnd()
{
OnEnd();
}
}
And now you WaitForDelegate will receive a IAnimation, so the class can handle any class that implements the IAnimation class
public class WaitForDelegate<T> where T : IAnimation
{
public WaitForDelegate(T animation)
{
animation.OnEnd += () => { Console.WriteLine("trigger"); };
}
}
Then we can test the code that we did with the following code
public static void Main(string[] args)
{
var a = new Animation();
var waitForDelegate = new WaitForDelegate<IAnimation>(a);
a.CallEnd();
}
The result is
trigger
Here is the working version on dotnetfiddle
https://dotnetfiddle.net/1mejBL
Important tip
If you are working with multithread, you must take some caution to avoid Null Reference Exception
Let's look again the CallEnd method that I've added for test
public void CallEnd()
{
OnEnd();
}
OnEnd event could have not value, and then if you try to call it, you will receive Null Reference Exception.
So if you are using C# 5 or lower, do something like this
public void CallEnd()
{
var #event = OnEnd;
if (#event != null)
#event();
}
With C# 6 it could be like that
public void CallEnd()
=> OnEnd?.Invoke();
More explanation, you could have this code
public void CallEnd()
{
if (OnEnd != null)
OnEnd();
}
This code that is above, probably make you think that you are safe from Null Reference Exception, but with multithread solution, you aren't. That's because the OnEnd event could be set to null between the execution of if (OnEnd != null) and OnEnd();
There is a nice article by Jon Skeet about it, you cann see Clean event handler invocation with C# 6
how can i implement this situation in RX without using subject. I've read a lot, and I just can't seem to figure it out
public class Member
{
public int Id { get; private set; }
public string Email { get; private set; }
public Member(string email)
{
this.Email = email;
}
}
public class MemberRepository
{
public void AddMember(Member member)
{
// save member
memberAdded.OnNext(member);
}
private Subject<Member> memberAdded = new Subject<Member>();
public IObservable<Member> MemberAdded { get { return memberAdded.AsObservable(); } }
}
public class MemberController
{
public void Create(Member item)
{
var repository = new MemberRepository();
var subs = repository.MemberAdded.Subscribe(x => SendMail(x));
repository.AddMember(item);
}
private void SendMail(Member value)
{
// send welcome mail
}
}
I've don't know how to initialize the IObservable MemberAdded because it is always null if it doesn't have the Subject backer nor do I know how to later call the OnNext at from a later function.
Lastly, is it a problem to have the observables as static properties and all the subscription code in one place?
The way I have implemented something similar is to expose a normal C# event MemberAdded on my MemberRepository. You can then use Observable.FromEvent or Observable.FromEventPattern (the difference is here) to subscribe to the event something like this:
public class MemberRepository
{
public void AddMember(Member member)
{
// save member
if (MemberAdded != null)
MemberAdded(new MemberEventArgs(member, MemberEvent.Add));
}
public event EventHandler<MemberEventArgs> MemberAdded;
}
...
Observable.FromEventPattern<MemberEventArgs>(h => memberRepository.MemberAdded += h,
h => memberRepository.MemberAdded -= h)
.Select(e => e.Member)
.Subscribe(m => Console.WriteLine("Member "+m+" added!));
In regard to your second question, you should avoid static properties - consider using something like the Event Aggregator pattern instead
What is the syntax to return an event from a function? (Not to call the event, to return it so that it can be bound to functions).
I have a container class that contains a dictionary where each members has an event.
The aim is to be able to write something like this:
Container c = new Container();
c.CreateEventForKey("a"); // Create the member in the dictionary
c.EventForKey("a") += some_function; // Bind some_function to the event in the "a" member
c.OnEventForKey("a","b"); // Calls some_function with argument "b"
The Container class looks like this:
public class Container {
public class Member {
public event Action<string> AnEvent;
public void OnEvent( string v ) { if(AnEvent!=null) { AnEvent(v); } }
}
protected Dictionary<string,Member> members;
// This seems to work OK.
public void OnEventForKey(string k, string v) {
if ( members.ContainsKey(k) ) { members[k].OnEvent(v); }
else { /* report error */ }
}
// Can't get this to compile.
public event Action<string> EventForKey(string k ) {
if ( members.ContainsKey(k) ) { return members[k].AnEvent; }
else { /* report error */ }
}
}
How can I define EventForKey so that this does what I expect?
What is the syntax to return an event from a function?
You can't, easily. Events - like properties - aren't really first class "objects" as such; they're members of a class. You don't really have a class member here - you're trying to just keep delegates in a dictionary.
You could create your own "event-like" container, but it's probably better to consider alternative designs, e.g.
c.Subscribe("a", SomeFunction);
c.OnEventForKey("a");
You might want to look at EventHandlerList for inspiration.
Why not simply return member and subscribe to it's event?
public IMember MemberForKey(string key) // return IMember
{
if (!members.ContainsKey(key))
throw new Exception();
return members[key];
}
And then subscribe:
Container c = new Container();
c.CreateEventForKey("a");
c.MemberForKey("a").AnEvent += some_function;
c.OnEventForKey("a", "b");
But you have public OnEvent method in Member class. In order to forbid raising events by client, you can create interface which will show only event. Just implement this interface by Member class:
public interface IMember
{
event Action<string> AnEvent;
}
And yes, you cannot return event, because actually event is not object, it is set of two methods add and remove, which add and remove delegates to inner field of delegate type. Here is how your event looks like:
private Action<string> _action; // field of delegate type
public event Action<string> AnEvent
{
add { _action += value; }
remove { _action -= value; }
}
Purpose of event is to provide only two operations for clients - adding and removing handlers. Delegate itself is hidden to clients. You can make it public:
public Action<string> _action;
But in this case any client can invoke it.
UPDATE: if you want to go with Subscribe/Remove syntax, then just use dictionary with handlers:
public class Container
{
private Dictionary<string, Action<string>> handlers =
new Dictionary<string, Action<string>>();
public void CreateEventForKey(string key)
{
// with empty handler added you can avoid null check
handlers.Add(key, (value) => { });
}
public void OnEventForKey(string key, string value)
{
if (!handlers.ContainsKey(key))
throw new Exception();
handlers[key](value);
}
public void Subscribe(string key, Action<string> handler)
{
if (!handlers.ContainsKey(key))
throw new Exception();
handlers[key] += handler;
}
}
Here's complete working example:
class Program
{
static void Main(string[] args)
{
Container c = new Container();
c.CreateEventForKey("a"); // Create the member in the dictionary
c.EventForKey("a").Add(str => Console.WriteLine(str));
c.EventForKey("a").Add(str => Console.WriteLine(str.ToUpper()));
c.OnEventForKey("a", "baa baa black sheep");
Console.ReadLine();
}
}
public class Container
{
public class Member
{
public List<Action<string>> AnEvent = new List<Action<string>>();
public void OnEvent(string v)
{
if (AnEvent != null)
{
this.AnEvent.ForEach(action => action(v));
}
}
public void AddEvent(Action<string> action)
{
this.AnEvent.Add(action);
}
}
protected Dictionary<string, Member> members = new Dictionary<string,Member>();
public void CreateEventForKey(string key)
{
this.members[key] = new Member();
}
// This seems to work OK.
public void OnEventForKey(string k, string v)
{
if (members.ContainsKey(k)) { members[k].OnEvent(v); }
else { /* report error */ }
}
public List<Action<string>> EventForKey(string k)
{
if (members.ContainsKey(k)) { return members[k].AnEvent; }
else { throw new KeyNotFoundException(); }
}
}
The difference is to behave similarly to an event by using a list of delegates.
I'm trying to fix a garbage collection problem of a MVVM application which uses the following model of Undo stack.
The example is very minimalistic and real world code is much different, uses a factory class of undo lists per ViewModel instead of a single undolist but is representative:
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.ComponentModel;
using System.Linq;
namespace ConsoleApplication9
{
public class UndoList
{
public bool IsUndoing { get; set; }
private Stack<Action> _undo = new Stack<Action>();
public Stack<Action> Undo
{
get { return _undo; }
set { _undo = value; }
}
private static UndoList _instance;
// singleton of the undo stack
public static UndoList Instance
{
get
{
if (_instance == null)
{
_instance = new UndoList();
}
return _instance;
}
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// execute the last undo operation
public void Undo()
{
UndoList.Instance.IsUndoing = true;
var action = UndoList.Instance.Undo.Pop();
action();
UndoList.Instance.IsUndoing = false;
}
// push an action into the undo stack
public void AddUndo(Action action)
{
if (UndoList.Instance.IsUndoing) return;
UndoList.Instance.Undo.Push(action);
}
// create push an action into the undo stack that resets a property value
public void AddUndo(string propertyName, object oldValue)
{
if (UndoList.Instance.IsUndoing) return;
var property = this.GetType().GetProperties().First(p => p.Name == propertyName);
Action action = () =>
{
property.SetValue(this, oldValue, null);
};
UndoList.Instance.Undo.Push(action);
}
}
public class TestModel : ViewModel
{
private bool _testProperty;
public bool TestProperty
{
get
{
return _testProperty;
}
set
{
base.AddUndo("TestProperty", _testProperty);
_testProperty = value;
}
}
// mock property indicating if a business action has been done for test
private bool _hasBusinessActionBeenDone;
public bool HasBusinessActionBeenDone
{
get
{
return _hasBusinessActionBeenDone;
}
set
{
_hasBusinessActionBeenDone = value;
}
}
public void DoBusinessAction()
{
AddUndo(() => { inverseBusinessAction(); });
businessAction();
}
private void businessAction()
{
// using fake property for brevity of example
this.HasBusinessActionBeenDone = true;
}
private void inverseBusinessAction()
{
// using fake property for brevity of example
this.HasBusinessActionBeenDone = false;
}
}
class Program
{
static void Test()
{
var vm = new TestModel();
// test undo of property
vm.TestProperty = true;
vm.Undo();
Debug.Assert(vm.TestProperty == false);
// test undo of business action
vm.DoBusinessAction();
vm.Undo();
Debug.Assert(vm.HasBusinessActionBeenDone == false);
// do it once more without Undo, so the undo stack has something
vm.DoBusinessAction();
}
static void Main(string[] args)
{
Program.Test();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
// at this point UndoList.Instance.Undo
// contains an Action which references the TestModel
// which will never be collected...
// in real world code knowing when to clear this is a problem
// because it is a singleton factory class for undolists per viewmodel type
// ideally would be to clear the list when there are no more references
// to the viewmodel type in question, but the Actions in the list prevent that
}
}
}
You see that when any viewModel goes out of scope the actions in the UndoList keep references to them. The real code groups various viewmodels into grouped undolists (viewModels that contain child viewmodels share the same undo stack), so it is difficult to know when and where to put the clearing.
I was wondering if there is some method to make those actions expire if they are the only one keeping references to the variables inside them?
Suggestions welcome!
I've got a solution for you. I don't like the use of the UndoList as a singleton, but I've kept it to provide you with a direct answer to your question. In practice I wouldn't use a singleton.
Now, you will find it very difficult to avoid capturing references to your view models in your actions. It would make your code very ugly if you tried. The best approach is to make your view models implement IDisposable and make sure that you dispose of them when they go out of scope. Remember that the garbage collector never calls Dispose so you must.
Using IDisposable is the standard model for cleaning up when an
instance is no longer needed.
So the first thing to define is a helper class that executes an action when it is disposed.
public sealed class AnonymousDisposable : IDisposable
{
private readonly Action _dispose;
private int _isDisposed;
public AnonymousDisposable(Action dispose)
{
_dispose = dispose;
}
public void Dispose()
{
if (Interlocked.Exchange(ref _isDisposed, 1) == 0)
{
_dispose();
}
}
}
Now I can write things like this to remove elements from lists:
var disposable = new AnonymousDisposable(() => list.Remove(item));
Later, when I call disposable.Dispose() the item is removed from the list.
Now here's your code re-implemented.
I've changed UndoList to be a static class, not a singleton. You can change it back if need be.
public static class UndoList
{
public static bool IsUndoing { get; private set; }
private static List<Action> _undos = new List<Action>();
public static IDisposable AddUndo(Action action)
{
var disposable = (IDisposable)null;
if (!IsUndoing)
{
disposable = new AnonymousDisposable(() => _undos.Remove(action));
_undos.Add(action);
}
return disposable ?? new AnonymousDisposable(() => { });
}
public static bool Undo()
{
IsUndoing = true;
var result = _undos.Count > 0;
if (result)
{
var action = _undos[_undos.Count - 1];
_undos.Remove(action);
action();
}
IsUndoing = false;
return result;
}
}
You'll notice that I've replaced the stack with a list. I did that because I need to remove items from inside the list.
Also, you can see that AddUndo now returns an IDisposable. Calling code needs to keep the return disposable and call Dispose when it wants to remove the action from the list.
I've also internalized the Undo action. It didn't make sense to have it in the view model. Calling Undo effectively pops the top item off of the list and executes the action and returns true. However, if the list is empty it returns false. You can use this for testing purposes.
The ViewModel class now looks like this:
public class ViewModel : IDisposable, INotifyPropertyChanged
{
public ViewModel()
{
_disposables = new List<IDisposable>();
_disposable = new AnonymousDisposable(() =>
_disposables.ForEach(d => d.Dispose()));
}
private readonly List<IDisposable> _disposables;
private readonly IDisposable _disposable;
public void Dispose()
{
_disposable.Dispose();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void AddUndo(Action action)
{ ... }
protected void SetUndoableValue<T>(Action<T> action, T newValue, T oldValue)
{ ... }
}
It implements IDisposable and internally, keeps track of a list of disposables and an anonymous disposable that will dispose of the items in the list when the view model itself is disposed of. Whew! A mouthful, but I hope that makes sense.
The AddUndo method body is this:
protected void AddUndo(Action action)
{
var disposable = (IDisposable)null;
Action inner = () =>
{
_disposables.Remove(disposable);
action();
};
disposable = UndoList.AddUndo(inner);
_disposables.Add(disposable);
}
Internally it calls UndoList.AddUndo passing in an action that will remove the returned IDisposable from the view model's list of undo actions when UndoList.Undo() is called - as well as, importantly, actually executing the action.
So this means that when the view model is disposed all of its outstanding undo actions are removed from the undo list and when Undo is called the associated disposable is removed from the view model. And this ensures that you are not keeping references to the view model when it is disposed of.
I created a helper function called SetUndoableValue that replaced your void AddUndo(string propertyName, object oldValue) method which wasn't strongly-typed and could cause you to have run-time errors.
protected void SetUndoableValue<T>(Action<T> action, T newValue, T oldValue)
{
this.AddUndo(() => action(oldValue));
action(newValue);
}
I made both of these methods protected as public seemed too promiscuous.
The TestModel is more-or-less the same:
public class TestModel : ViewModel
{
private bool _testProperty;
public bool TestProperty
{
get { return _testProperty; }
set
{
this.SetUndoableValue(v => _testProperty = v, value, _testProperty);
}
}
public bool HasBusinessActionBeenDone { get; set; }
public void DoBusinessAction()
{
this.AddUndo(this.inverseBusinessAction);
businessAction();
}
private void businessAction()
{
this.HasBusinessActionBeenDone = true;
}
private void inverseBusinessAction()
{
this.HasBusinessActionBeenDone = false;
}
}
And finally, here's the code that tests the UndoList functions correctly:
using (var vm = new TestModel())
{
Debug.Assert(UndoList.Undo() == false);
vm.TestProperty = true;
Debug.Assert(UndoList.Undo() == true);
Debug.Assert(UndoList.Undo() == false);
Debug.Assert(vm.TestProperty == false);
vm.DoBusinessAction();
Debug.Assert(UndoList.Undo() == true);
Debug.Assert(vm.HasBusinessActionBeenDone == false);
vm.DoBusinessAction();
}
Debug.Assert(UndoList.Undo() == false);
Please let me know if I can provide any more detail on anything.
If you can't clean it up any other way you could use WeakReference to hold property, but I think there would be other issues because this would still cause a Action instance to exist with a null reference attached to it.
As a quick look I would be more inclined to use the singleton to hold a registration to the model and let the model manage a instance list of all the undo actions attached to it. When the model goes out of scope call a clean-up method on it or implement a IDisposable type interface on it may if this fits. However depending on the implementation you may not need the singleton anyway.
I have a class that takes an observable in its constructor, then subscribes to it and does some stuff, sets properties etc. The class itself is observable.
I want to subscribe to my source observable only if someone is subscribed to my class, but I can't figure out how to do it.
public MyClass : IObservable<MyResult>
{
private readonly Subject<MyResult> _subject = new Subject<MyResult>();
private readonly IConnectableObservable<MySource> _source;
public MyClass(IObservable<MySource> source)
{
_source = source
//All my logic to set properties and such
//goes here as a side effect, instead of in a subscription...
.Do(...)
//I hope that by publishing, side effects will happen only once...
.Publish();
}
public IDisposable Subscribe(IObserver<MyResult> observer)
{
return new CompositeDisposable(
_source.Subscribe(/*
don't have anything to do here,
just subscribing to make sure I'm subscribed to source...
(this can't be the right way to do it)
*/),
_subject.Subscribe(observer));
}
}
UPDATE
#Scott: I can see why implementing IObservable would be an anti-pattern. My Class needs to consume a single observable, and exposes 3 as properties (originally the most commonly used observable was going to be returned by MyClass itself, but I think that having it as a property might be better.
What I'm trying to write is an observable ICommand. I know some exist, but this is more of a way to learn Rx...
public class ObservableCommand<T> : ICommand
{
private readonly ISubject<T> _executeRequests = new Subject<T>();
private readonly ISubject<T> _canExecuteRequests = new Subject<T>();
public IObservable<bool> CanExecuteChanges { get; private set; }
public IObservable<T> CanExecuteRequests { get; private set; }
public IObservable<T> ExecuteRequests { get; private set; }
public ObservableCommand(IObservable<bool> canExecute)
{
var source = canExecute.DistinctUntilChanged()
//How do I dispose of subscription later?
//I have this fear that I'm going to have a chain of references,
//and my entire app will never get GC'd!
var subscription = source.Subscribe(
o => {
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
});
CanExecuteChanges = source;
CanExecuteRequests = _canExecuteRequests.AsObservable();
ExecuteRequests = _executeRequests.AsObservable();
}
#region ICommand Members
public bool CanExecute(object parameter)
{
_canExecuteRequests.OnNext(parameter is T ? (T)parameter : default(T));
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_executeRequests.OnNext(parameter is T ? (T)parameter : default(T));
}
#endregion
}
How about just not Doing or Publishing in the constructor, but rather in the Subscribe method?
It should be said, explicitly implementing IObservable<T> is something of an Rx anti-pattern.
You can make Subscriptions dependent on other subscribers with Defer and Create, something like
IObservable<MySource> source;
IObservable<MySource> sourceWithSubSideEffect = Observable.Defer(() =>
{
// Do something interesting on Subscription
// ....
return source;
});
I've prepared a snipped for you. MyClass implements IObservable<T> and has also methods of IObserver<T> but they are all private. With additional OnInitialize and OnSubscribe you should be able to do whatever you want on any event you want to response to.
If you want to make this snipped reusable you could define all methods as partial as they all return void. Then you could create definition to whatever you want.
public class MyClass<T> : IObservable<T>
{
private readonly IObservable<T> m_Source;
public MyClass(IObservable<T> source)
{
if (source == null) throw new ArgumentNullException("source");
m_Source = source.Do(OnNext, OnError, OnCompleted);
OnInitialize();
}
public IDisposable Subscribe(IObserver<T> observer)
{
OnSubscribe();
return m_Source.Subscribe(observer);
}
private void OnInitialize()
{
Console.WriteLine("OnInitialize");
}
private void OnSubscribe()
{
Console.WriteLine("OnSubscribe");
}
private void OnNext(T value)
{
Console.WriteLine("OnNext: {0}", value);
}
private void OnError(Exception error)
{
Console.WriteLine("OnError: {0}", error.Message);
}
private void OnCompleted()
{
Console.WriteLine("OnCompleted");
}
}