Click here to Skip to main content
15,890,825 members
Articles / Programming Languages / C#
Article

The best of reactive framework extensions

Rate me:
Please Sign up or sign in to vote.
4.98/5 (16 votes)
20 Feb 2014LGPL32 min read 46.4K   504   44   32
An enumeration of nice code rewrite of recurring spaghetti code thanks to Reactive Framework

Introduction  

Some of you may remember my love with Rx Framework began with a pet project I wrote about.
Now, I’m using Rx Framework almost every day, but this is thanks to a small little personal framework that made my life easier.

So, no big and complicated article today, it’s friday after all, just an enumeration of my best reactive framework extensions methods.  This article can be considered as the suite of this old one that grouped some of my utility classes.

Best of 

All methods I use such as .Subscribe will return a IDisposable that you can dispose if you want to unsubscribe. 

How to subscribe to PropertyChanged

When you are interested into a property that change on some of you view model, you often need to write the following spaghetti code :

C#
public void UpdateUser(User user)
{
    user.PropertyChanged += user_PropertyChanged;
    NameChanged();
}
void user_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if(e.PropertyName == "Name")
        NameChanged();
}
private void NameChanged()
{
    //Do wonderfull stuff

I replaced with 

C#
user
    .ItemPropertyChanged(u=>u.Name,true)
    .Subscribe((args) =>
    {
        //Do wonderfull stuff with args.NewValue and args.OldValue
    });

The boolean fire the subscribe action directly a first time when you subscribe. Note that you can get the NewValue and OldValue of your property with the args argument, and do not have magic string anymore.

How to subscribe to CollectionChanged

Replace the old way of doing something on each items added to an observable collection :

C#
public void SubscribeToCollection(ObservableCollection<User> users)
{
    users.CollectionChanged += users_CollectionChanged;
    foreach(var user in users)
    {
        NewUser(user);
    }
}
void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if(e.NewItems != null)
        foreach(User user in e.NewItems)
        {
            NewUser(user);
        }
}
private void NewUser(User user)
{
    //Do something
}

Into the reactive way:

C#
users
    .ForeachItem()
    .Subscribe(user =>
    {
        //Do something
    });

How to subscribe to items in a ObservableCollection

Well basically a spaghetti code combining the two previous old approach. I replaced the following spaghetti.

C#
public void SubscribeToCollection(ObservableCollection<User> users)
{
    users.CollectionChanged += users_CollectionChanged;
    foreach(var user in users)
    {
        NewUser(user);
    }
}
void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if(e.NewItems != null)
        foreach(User user in e.NewItems)
        {
            NewUser(user);
        }
    if(e.OldItems != null)
    {
        foreach(User user in e.OldItems)
        {
            user.PropertyChanged -= UserChanged;
        }
    }
}
private void NewUser(User user)
{
    user.PropertyChanged += UserChanged;
}
void UserChanged(object sender, PropertyChangedEventArgs e)
{
    //Do wonderfull stuff when one user changes
}

Into the clean version

C#
users
    .ObserveEachItem(u => u.ItemPropertyChanged())
    .Subscribe(args =>
    {
        User user = args.Sender;
        //Do wonderfull stuff when one user changes
    });

Weak event listener

Have you tried to subscribe to an object weakly, so the garbage collector correctly garbage a listener that is no longer referenced anywhere ? Well, one solution is to read this msdn page and use the WeakEventManager. Sorry, I’m too lazy to read it for you.

The other solution is to use my ObserveWeakly method :

C#
var subscription = user.ItemPropertyChanged()
                    .ObserveWeakly()
                    .Subscribe(args =>
                    {
                        User u = args.Sender;
                        //User changed do what you want...
                    });
subscription = null; //OMG forgot to Dispose the subscription !!! memory leak !1!
GC.Collect();
user.AssertSubscribers(0); //Just kidding 

Subscribe to dependency property changes

The code to subscribe to dependency properties is not trivial as you can see in the following snippet.

C#
TextBox box = new TextBox();
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(TextBox.BackgroundProperty, typeof(TextBox));
if(dpd != null)
{
    dpd.AddValueChanged(box, (sender, args) =>
    {
        //Do wonderfull stuff when the background changed
    });
}

But fear not, now you can do that.

C#
TextBox box = new TextBox();
box.DependencyPropertyChanged<Brush>(TextBox.BackgroundProperty)
    .Subscribe(args =>
    {
        Brush brush = args.NewValue;
    });

Synchronize two ObservableCollection

I’m sure it happened to you more than once, for example you want an ObservableCollection<String> that list dynamically all the girls’ name of your ObservableCollection<User>. Here is the old way :

C#
ObservableCollection<String> GirlsNames = new ObservableCollection<String>();
public void SubscribeToCollection(ObservableCollection<User> users)
{
    users.CollectionChanged += users_CollectionChanged;
    foreach(var user in users)
    {
        NewUser(user);
    }
}


void users_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if(e.NewItems != null)
        foreach(User user in e.NewItems)
        {
            NewUser(user);
        }
    if(e.OldItems != null)
    {
        foreach(User user in e.OldItems)
        {
            GirlsNames.Remove(user.Name);
        }
    }
}
private void NewUser(User user)
{
    if(user.Gender = Gender.Girl)
        GirlsNames.Add(user.Name);
}

Now you can turn this spaghetti into a single line: (Always returns a IDisposable if you want to stop the mapping)

C#
users.MapToCollection(GirlsNames, u=>u.Name, u=> u.Gender == Gender.Girl);

Conclusion

I put the sources and binaries as this article, I also have a private GIT repository and internal Nuget feed to deploy my framework in my own projects. I intend to switch to public GIT repository and public nuget feed, if you beg me. ;)

Anyway the solution ships with full unit tests for everything, feel free to play with it, if there is more that what you need don’t hesitate to just copy/paste my code if you don’t want all.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer Freelance
France France
I am currently the CTO of Metaco, we are leveraging the Bitcoin Blockchain for delivering financial services.

I also developed a tool to make IaaS on Azure more easy to use IaaS Management Studio.

If you want to contact me, go this way Smile | :)

Comments and Discussions

 
QuestionIssue Pin
Clifford Nelson5-Apr-18 11:17
Clifford Nelson5-Apr-18 11:17 
GeneralMy vote of 5 Pin
D V L11-Jan-16 2:56
professionalD V L11-Jan-16 2:56 
QuestionQuestion regarding your ObserveEachItem extension Pin
Sacha Barber12-Mar-14 0:09
Sacha Barber12-Mar-14 0:09 
AnswerRe: Question regarding your ObserveEachItem extension Pin
Nicolas Dorier12-Mar-14 2:11
professionalNicolas Dorier12-Mar-14 2:11 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Sacha Barber12-Mar-14 2:43
Sacha Barber12-Mar-14 2:43 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Sacha Barber9-May-14 1:24
Sacha Barber9-May-14 1:24 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Nicolas Dorier9-May-14 1:35
professionalNicolas Dorier9-May-14 1:35 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Sacha Barber9-May-14 22:06
Sacha Barber9-May-14 22:06 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Nicolas Dorier10-May-14 0:27
professionalNicolas Dorier10-May-14 0:27 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Sacha Barber10-May-14 3:06
Sacha Barber10-May-14 3:06 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Nicolas Dorier10-May-14 3:43
professionalNicolas Dorier10-May-14 3:43 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Sacha Barber10-May-14 7:10
Sacha Barber10-May-14 7:10 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Sacha Barber10-May-14 7:21
Sacha Barber10-May-14 7:21 
GeneralRe: Question regarding your ObserveEachItem extension Pin
Nicolas Dorier10-May-14 9:36
professionalNicolas Dorier10-May-14 9:36 
GeneralMy vote of 5 Pin
Joachim Blank25-Feb-14 1:48
Joachim Blank25-Feb-14 1:48 
GeneralRe: My vote of 5 Pin
Nicolas Dorier25-Feb-14 2:17
professionalNicolas Dorier25-Feb-14 2:17 
QuestionFor us older coders: Pin
Jerry W. Manweiler, Ph.D.24-Feb-14 8:08
Jerry W. Manweiler, Ph.D.24-Feb-14 8:08 
AnswerRe: For us older coders: Pin
Nicolas Dorier24-Feb-14 8:43
professionalNicolas Dorier24-Feb-14 8:43 
It is essential to be clear about what you are searching for when looking for performance, as it is always context related.
Given 2 ways of doing the same thing, one take 1ms the other 100ms what would you prefer ? It depends. The response does not lie in the metric, but in the purpose.
If it happens once every minute, you don't care, the user will not notice, so better look at another criteria to make your decision.

I can give you some metric, but I can't tell you if they are good for you.
In another article of mine GestSpace : A case for Rx Framework and Leap Motion[^] I process around 700 objects every second with RX Framework. Can it be better performance wise ? Definitively. Do I care ? No, as long as it works on the user machine. Also the rate was limited by the 60 FPS rate of the leap motion.

However, if you don't understand the lambda do not use RX. People knowing lambda will find my way of doing thing way easier to read.
RX on its own takes some time to master. Lambda is also another beast.

As to the question of whether or not you should learn lambda, I will respond it depends on how much time per day you use C#. If you are doing a quick and dirty scripts for simple recurring admin task, or that you are mainly a developer in C99, you don't.
If you are an everyday C# developer, then I consider critical you and your team use it. All language, even C++ and soon -if not already- Java, have lambda nowadays.

If your boss does not give you enough time, it is your duty to stay up to date with your tools, or you will feel stuck in a rabbit hole you can't leave very soon.
GeneralRe: For us older coders: Pin
Jerry W. Manweiler, Ph.D.24-Feb-14 9:36
Jerry W. Manweiler, Ph.D.24-Feb-14 9:36 
GeneralRe: For us older coders: Pin
Nicolas Dorier24-Feb-14 11:15
professionalNicolas Dorier24-Feb-14 11:15 
QuestionMy vote of -1 Pin
Paulo Zemek23-Feb-14 15:35
mvaPaulo Zemek23-Feb-14 15:35 
AnswerRe: My vote of -1 Pin
Nicolas Dorier24-Feb-14 0:08
professionalNicolas Dorier24-Feb-14 0:08 
QuestionI use Rx a lot in my code Pin
Sacha Barber21-Feb-14 0:38
Sacha Barber21-Feb-14 0:38 
AnswerRe: I use Rx a lot in my code Pin
Nicolas Dorier21-Feb-14 1:50
professionalNicolas Dorier21-Feb-14 1:50 
AnswerRe: I use Rx a lot in my code Pin
Nicolas Dorier24-Feb-14 0:10
professionalNicolas Dorier24-Feb-14 0:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.