17

Let's say, we have a class:

public class Foo
{
   public string Do(int param)
   {
   }
}

I'd like to create an observable of values that are being produced by Do method. One way to do it would be to create an event which is being called from Do and use Observable.FromEvent to create the observable. But somehow I don't feel good about creation of an event just for the sake of the task. Is there a better way to do it?

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Sergey Aldoukhov
  • 22,316
  • 18
  • 72
  • 99

5 Answers5

9

Matt's answer made me thinking about this:

public class Foo
{
    private readonly Subject<string> _doValues = new Subject<string>();

    public IObservable<string> DoValues { get { return _doValues; } }

    public string Do(int param)
    {
        var ret = (param * 2).ToString();
        _doValues.OnNext(ret);
        return ret;
    }
}


var foo = new Foo();
foo.DoValues.Subscribe(Console.WriteLine);
foo.Do(2);
Sergey Aldoukhov
  • 22,316
  • 18
  • 72
  • 99
  • 5
    If this is what you are really looking for then, you should at least protect your internal state by changing one line to public IObservable DoValues { get { return _doValues.AsObservable(); } } . This will prevent consumers, just casting your IObservable back to an ISubject and OnNext-ing their own values. :( – Lee Campbell Feb 13 '12 at 17:59
1

I'm assuming you control the Foo class, since you're talking about adding an event to it as one option. Since you own the class, is there any reason you can't define your own IObservable implementation for the Do method?

public class Foo
{
    DoObservable _doValues = new DoObservable();

    public IObservable<String> DoValues
    {
        return _doValues;
    }

    public string Do(int param)
    {
        string result;
        // whatever
        _doValues.Notify(result);
    }
}

public class DoObservable : IObservable<String>
{
    List<IObserver<String>> _observers = new List<IObserver<String>>();

    public void Notify(string s)
    {
        foreach (var obs in _observers) obs.OnNext(s);
    }

    public IObserver<String> Subscribe(IObserver<String> observer)
    {
        _observers.Add(observer);
        return observer;
    }
}

Your class now has an Observable<String> property which provides a way to subscribe to the values returned from the Do method:

public class StringWriter : IObserver<String>
{
    public void OnNext(string value)
    {
        Console.WriteLine("Do returned " + value);
    }

    // and the other IObserver<String> methods
}

var subscriber = myFooInstance.DoValues.Subscribe(new StringWriter());
// from now on, anytime myFooInstance.Do() is called, the value it 
// returns will be written to the console by the StringWriter observer.

I've not dabbled too much into the reactive framework, but I think this is close to how you would do this.

Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
  • You can make your DoObserver class an inner (nested) class of the Foo class, too, to keep it as a closed system. – Matt Hamilton Feb 04 '10 at 05:16
  • Also see http://stackoverflow.com/questions/1768974/implementing-iobservablet-from-scratch, which has a more complete implementation including supporting unsubscribing via IDisposable. – Matt Hamilton Feb 04 '10 at 05:21
  • 2
    Your DoObservable is just Subject, no need to reinvent the wheel – Ana Betts Feb 13 '12 at 22:53
0

Observable.Generate solves your problem, although you don't need to use the condition and the continue for your sample. This just creates and object and continually returns the result of the call to DoIt():

Observable.Generate (
  new Foo(),
  item => true, // in your example, this never terminates
  item => item, // we don't actually do any state transitions
  item => { return item.DoIt(); }  // This is where we emit our value
  );

In practice, you often do want to track some state and have some termination condition. Generate makes it easy.

James Moore
  • 8,636
  • 5
  • 71
  • 90
0

Generally you want to avoid using Subjects. In my experiance it is a sign you are doing something wrong. Observable.Create or perhaps Generate are normally better fits.

I would be interested to see what you are actually trying to do to see if we can provide a better answer. Lee

Lee Campbell
  • 10,631
  • 1
  • 34
  • 29
  • 1
    I disagree, Observable.Create doesn't enforce Rx semantics, you have to do it yourself - it's easier to use Subjects even if it's less Functionally Correct™. Create is good when you need it, but it's often overkill. – Ana Betts Feb 13 '12 at 22:55
  • 2
    Subject is a "bridge" between functional and object oriented worlds provided by RX. So unless your code is 100% functional, subjects are quite useful. – Sergey Aldoukhov Feb 13 '12 at 23:07
  • 1
    Agree to disagree. Having used Rx for 2years on a commercial project with a group of smart guys we all came to the conclusion that Subjects are a code smell, not always wrong, but generally a point of discussion in a code review. Also a shame that people are encouraging the @Sergey and not pointing out the problems with his code (specifically .AsObservable()). Only trying to help. – Lee Campbell Feb 14 '12 at 08:20
  • @lee-campbell Don't judge code by "smell"... I never had a single problem with subjects, so I'm talking from experience. Same goes to your comment on casting property to ISubject - while it is possible, there is absolutely no reason to do so. BTW, you're welcome to create a question on "Why RX Subjects are to be avoided" - I'll be interested to see the responces and you'll get a ton of rep. – Sergey Aldoukhov Feb 14 '12 at 19:19
  • Cheers, good idea. Also would keen to see what the Rx Forums think. There seems to be much more Rx traffic there and some great ideas bouncing around. – Lee Campbell Feb 15 '12 at 08:59
  • I went to create that post, but the Author of Rx has spoken. http://social.msdn.microsoft.com/Forums/en-US/rx/thread/bbf87eea-6a17-4920-96d7-2131e397a234 – Lee Campbell Feb 15 '12 at 09:07
  • @lee-campbell "Author of Rx has spoken" is a bit religious to me ;) Here we go: http://stackoverflow.com/questions/9299813/rx-subjects-are-they-to-be-avoided – Sergey Aldoukhov Feb 15 '12 at 19:22
-2

I would look at the EventAggregator model. There's a great article with sample code that shows an implementation with StructureMap.

http://www.codeproject.com/KB/architecture/event_aggregator.aspx

Corey Coogan
  • 1,569
  • 2
  • 17
  • 31
  • There is a context that points to RX - the tag, the reference to an existing solution of using Observable.FromEvent. Anyway, from now on the only "real" IObservable is the one that comes from System.Reactive namespace. – Sergey Aldoukhov Feb 06 '10 at 07:28
  • In fact IObservable is in the System namespace and is built into .NET 4 and .NET 4.5 – Lee Campbell Feb 13 '12 at 18:02