Observing .NET with IObservable
Erik Meijer and Brian Beckman introduced the concept of IObservable for .NET in their Channel 9 video “Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx)”, which is getting included in .NET version 4. .NET events present a number of challenges to developers. Weak event managers, like the Composite Framework’s (Prism) “EventAggregator”, act as an event publish-subscribe proxy to decouple direct event references for better memory garbage collection. Silver Bay Labs did a good overview video on the subject, but Microsoft’s MSDN technical concepts documentation accurately describes the problem as well (see the section under “Subscribing Using Strong References”).
The Quick Problem Summary: .NET events are usually directly referenced through multi-cast delegates (a collection of classes that act like function pointers). I would call these references pointers, but these are managed references and this is important because managed references are managed through .NET garbage collection. When an object (”observer”) references an event on an object (”observable”) and the observable object goes out of scope (kind of), .NET garbage collection will not clean-up the observable, because of the observer’s event reference. Got it? You almost need a “Disposing” event on observable objects, so that observers can be notified and properly de-reference the subscribed event.
What’s the Point? … First a Major WPF/Silverlight Eventing Digression
I did a lot of WPF development earlier this year with the Model-View-ViewModel pattern. A lot of the classes in this pattern will implement the INotifyPropertyChanging and INotifyPropertyChanged. There is a lot of great debate around implementing these notification interfaces versus using the WPF DependencyProperty. Ultimately, you end up with all of the public properties in these classes looking something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public abstract class BaseModel : INotifyPropertyChanging, INotifyPropertyChanged { public event PropertyChangingEventHandler PropertyChanging; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanging(string propertyName) { if (this.PropertyChanging != null) { this.PropertyChanging( this, new PropertyChangingEventArgs(propertyName)); } } protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged( this, new PropertyChangedEventArgs(propertyName)); } } } public class BusinessModel : BaseModel { private string value; public string Value { get { return this.value; } set { this.OnPropertyChanging("Value"); this.value = value; this.OnPropertyChanged("Value"); } } } |
String Madness! That’s right! You’ve got strings everywhere, so uninstall your code refactoring utilities, because they are now useless. In fact, the Joy of Code released a model unit test framework for just this kind of problem (along with other not-so-useless features).
A Strongly-Typed Solution: There are more complex and robust solutions being developed on CodePlex, but they hadn’t worked out a solution at the time I wrote my own. So, we can improve this stringy solution with some effective use of .NET Lambda expressions… (Super-Secret Tip: download the source for the unit testing framework Moq and review the “Setup” and “Verify” methods.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | public abstract class BaseModel : INotifyPropertyChanging, INotifyPropertyChanged { public event PropertyChangingEventHandler PropertyChanging; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanging<T>(Expression<Func<T>> expression) { var memberExpression = expression.Body as MemberExpression; if (this.PropertyChanging != null) { this.PropertyChanging( this, new PropertyChangingEventArgs(memberExpression.Member.Name)); } } protected virtual void OnPropertyChanged<T>(Expression<Func<T>> expression) { var memberExpression = expression.Body as MemberExpression; if (this.PropetyChanged != null) { this.PropertyChanged( this, new PropertyChangedEventArgs(memberExpression.Member.Name)); } } } public class BusinessModel : BaseModel { private string value; public string Value { get { return this.value; } set { this.OnPropertyChanging(() => this.Value); this.value = value; this.OnPropertyChanged(() => this.Value); } } } |
Still Interested in IObservable?
Because of my experience with the problems in .NET event references and the INotifyPropertyChang[...] interface implementations, I wrote a new solution called “IObservable”. Sweet! Now, circle back to the start of this post… Doh! Erik told the world about his IObservable and then Microsoft baked it into .NET 4 in the reactive framework. What’s more, their implementation looks nothing like what I did.
The Notification Solution Described
Download the zip file above and review the C# code file. The quickest description would be an example…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | public class MyObservable : MemberNotifier // all the goodies { private string value; public string Value { get { return this.value; } set { this.OnChanging(() => this.Value); this.value = value; this.OnChanged(() => this.Value); } } } public class ObservableTestFixture { [Fact] // xUnit test public void TestMemberNotifications() { var observable = new MyObservable(); int changingCount = 0, changedCount = 0; string changingPropertyName = null, changedPropertyName = null; // the anonymous delegate is passed directly into the function call // so, there are no managed references to clean up // However, you should use "UnregisterObserver" if the delegate is assigned to an object instance observable.RegisterObserver((o, args) => { // for the record, I don't like these "is" checks... it's most definately smelly code // however, the interface for "args" can be implemented using any method (enums, bools, etc.) if (args is MemberChangingNotification) { changingCount++; changingPropertyName = arg.MemberExpression.Member.Name; } else if (args is MemberChangedNotification) { changedCount++; changedPropertyName = arg.MemberExpression.Member.Name; } }); observable.Value = "test"; // strongly-typed sweetness var expectedPropertyName = ExpressionHelper.GetMember(() => see.Value).Name; Assert.AreEqual(1, changingCount); Assert.AreEqual(expectedPropertyName, changingPropertyName); Assert.AreEqual(1, changedCount); Assert.AreEqual(expectedPropertyName, changedPropertyName); } } |
Performance? Because I use the reflection operations from the lambda expressions, I’d be curious to know if anyone has experienced performance degradation of any sort with this solution. The .NET team has done so much to improve reflection lookups that it’s usually not a problem, even in the enterprise.
Event Driven Architecture
Yesterday, I finished reading “Event Processing in Action” by Manning Publishing (not yet published to the public at the time of this post), which effectively describes the considerations that need to be made in Event Driven Architecture. The book covers the topic with examples in four different languages, Aleri, Apama, Esper, and Etalis. Because these languages stand alone in many respects, it’s a refreshing perspective to step outside of Java or .NET and consider the many possibilities. The book was a solid read and I would recommend it to anyone interested in the subject. Of course, the book’s content has already begun to spin my head gears into thinking through the problems and solutions outlined here for even better solutions.
What do you think?
Please comment and leave your thoughts on this solution, on Microsoft’s Reactive framework, weak eventing and the Composite Framework’s EventAggregator, etc.
Updates
Later today (WPF/Silverlight Notifications):
A much respected ex-coworker of mine suggested more could be done to simplify property setters and mentioned that Glenn Block was working on a solution that might be included in the Managed Extensibility Framework. I haven’t reached out to Glenn yet, but here’s a quick and untested shot for WPF and Silverlight:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | // I'll take a shot at Wordpress coding... where's my intellisense?! public abstract class PropertyNotifier : MemberNotifier, INotifyPropertyChanging, INotifyPropertyChanged { // the only requirement for INotifyPropertyChanging public event PropertyChangingEventHandler PropertyChanging; // the only requirement for INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected override void OnChanging<T>(Expression<Func<T>> expression) { base.OnChanging(expression); // notify using the Observable pattern if (this.PropertyChanging != null) { var memberExpression = expression.Body as MemberExpression; // notify using events this.PropertyChanging( this, new PropertyChanging(memberExpression.Member.Name)); } } protected void OnChanged<T>(Expression<Func<T>> expression) { base.OnChanged(expression); // notify using the Observable pattern if (this.PropertyChanged != null) { var memberExpression = expression.Body as MemberExpression; // notify using events this.PropertyChanged( this, new PropertyChanged(memberExpression.Member.Name)); } } // wrap a setter in Changing and Changed notifications protected void SetWithNotifications<T>( Expression<Func<T>> expression, Action setter) { this.OnPropertyChanging(expression); setter(); this.OnPropertyChanged(expression); } } // now, implementing becomes incredibly simple! public class MyObservable : PropertyNotifier { private string value; public string Value { get { return this.value; } set { // pass the member expression and the setter action delegate this.SetWithNotifications(() => this.Value, () => this.value = value); } } } // what about multiple notifications? public class MyObservablePerson : PropertyNotifier { private string firstName; private string lastName; public string FirstName { get { return this.firstName; } set { // ... [input validation ?] ... this.SetWithNotifications( () => this.FirstName, // member expression () => // setter { // wrap setter with change notifications for FullName this.OnPropertyChanging(() => this.FullName); this.firstName = value; this.OnPropertyChanged(() => this.FullName); }); } public string LastName { get { return this.lastName; } set { // ... [input validation] ... this.SetWithNotifications( () => this.LastName, () => { this.OnPropertyChanging(() => this.FullName); this.lastName = value; this.OnPropertyChanged(() => this.FullName); }); } public string FullName { get { return String.Format( CultureInfo.CurrentCulture, "{1}, {0}", this.FirstName, this.LastName); } } } } /* FYI: You could also overload the SetWithNotifications method to accept a primary member expression for the setter and a list of secondary member expressions for notification... The call would end up looking something like: this.SetWithNotifications( () => this.Value, { () => this.DependantValue1, () => this.DependantValue2, () => this.DependantValue3 }, this.value = value); The method could simply iterate the dependencies, calling Changing() and Changed() for each. */ |


5 Responses to “Observing .NET with IObservable”
December 22nd, 2009 at: 2:31 pm
Excellent post..Keep them coming
Thanks for sharing.
January 16th, 2010 at: 5:01 am
Just wanted to let you know that the download seems to be broken. Looks interesting though, thanks!
January 18th, 2010 at: 9:44 am
Horst - Fixed the download link. Thanks!
July 7th, 2010 at: 3:18 pm
Hey, great blog!. How do I subscribe to your RSS feed to ensure I get notifed when you make new posts? Thank you
July 7th, 2010 at: 4:00 pm
A modern browser like Google Chrome, FireFox, IE7 or IE8 should pick up the feed automatically for you to add. Other feed readers will also auto-pick-up the feed if you provide the URL to the blog. The direct link is: http://blogs.us.sogeti.com/ericswanson/feed/
Leave a Reply