Background
C# offers many features of the functional languages. One of them is the Extension Methods. This fantastic feature allows solving some non-trivial problems in very elegant ways.
However, I always felt that the type extensibility model was not complete without Extension Properties. Apparently, I am not alone. Now and then, I see the articles/solutions for mimicking Extension Properties. Initially, I thought that Microsoft would eventually deliver a proper Extension Properties implementation but now it is apparent that it's very unlikely.
To be technically accurate, XAML Attached Properties allows extending types with the property-like dynamic "members". However, the Attached Properties implementation is relatively heavy, too XAML specific and the usage pattern isn't so great neither.
Thus the presented solution is an attempt to implement generic Attached Properties based on the extension methods syntax.
While hoping for a proper solution from Microsoft, I developed and used my own, which achieves the desired behavior. Though it is lacking the syntactical sugar possible only by truly extending the C# syntax, it served me very well and I decided to share it.
The trigger for this article was another article on the very same subject:
It is a very good solid article. The author got my 5. He explained very well the objective he was trying to achieve and provided a robust working solution for the problem. However, I felt that his solution was a bit over engineered and practically the same behavior could be achieved with a much simpler implementation.
The Solution
How It Works
The solution is straight forward. It is built around the dictionary
, which associates the instance of an object with a collection of the named values (key/pair). The dictionary is hosted by the AttachedProperies
static
class, which allows setting and getting named values to the instance of the object via Extension Methods:
static class AttachedProperies
{
public static Dictionary<WeakReference, Dictionary<string, WeakReference>> ObjectCache;
public static void SetValue<T>(this T obj, string name, object value)...
public static T GetValue<T>(this object obj, string name)...
ObjectCache
references the objects with WeakReferences
for avoiding memory leaks. The name of the class (AttachedProperties
) is deliberate as it mimics the XAML Attached Properties. The API relies on extension methods so the usage pattern can be as simple as follows:
var animation = (Storyboard)FindResource("Storyboard1");
animation.SetValue("StartTime", DateTime.Now);
animation.Begin();
.....
void OnStoryboard_Complete(object sender,....)
{
var animation = (Storyboard)sender;
DateTime startTime = animation.GetValue<DateTime>("StartTime");
Of course, using string
literals isn't the cleanest approach. Thus, I would recommend to group properties by the target type in the static
classes containing extension methods. This allows strongly typed and readable syntax:
static class StoryboardExtensions
{
public static DateTime GetStartTime(this Storyboard obj)
{
return obj.GetValue<DateTime>("StartTime");
}
public static void SetStartTime(this Storyboard obj, DateTime value)
{
obj.SetValue("StartTime", value);
}
}
And the usage:
animation.SetStartTime(DateTime.Now);
...
DateTime startTime = animation.GetStartTime();
The solution also solves an interesting problem - collecting the weak references to the already disposed objects. The routine which does this is the AttachedProperties.Collect
method. Collect
is to be invoked either explicitly or automatically depending on the AttachedProperties.MemoryManagementMode
:
Progressive
Collect
will be called automatically every time when number of the "weakly-referenced" instances doubles (since the last Collect
call). GCSynchronized
Collect
will be called automatically when Garbage Collector collects unreferenced resources. OnAllocate
Collect
will be called automatically with every AttachedProperties.SetValue<T>(...)
call. Manual
Collect
will be called explicitly from the host code.
That is it! This is roughly all about the solution. The source code can be found in the article downloadables (AttachedProperties_original.cs).
The presented solution despite some conceptual similarities has significant differences comparing to the other solution I mentioned in the introduction:
- The implementation is much leaner (~150 lines of code).
- The timer based garbage collection mechanism present in the alternative solution seems too simplistic to me. So I have implemented the event driven collection mechanism.
- The presented solution deliberately does not offer any discovery mechanism. In my opinion, using the
TypeDesciptorProvider
in the alternative solution does not really bring any practical value. Thus I decided not to invest in this feature.
Revising the Solution
My original implementation was based on the WeakReference
dictionary. However in .NET 4.0, there is a more suitable collection type for this - ConditionalWeakTable
. This class is capable of fully automatic removal of all references to the instances no longer referenced anywhere else. Because there is no need for any memory management any more, the whole solution can be collapsed to the ~30 lines of code. The following is the final revised solution:
public static class AttachedProperies
{
public static ConditionalWeakTable<object,
Dictionary<string, object>> ObjectCache = new ConditionalWeakTable<object,
Dictionary<string, object>>();
public static void SetValue<T>(this T obj, string name, object value) where T : class
{
Dictionary<string, object> properties = ObjectCache.GetOrCreateValue(obj);
if (properties.ContainsKey(name))
properties[name] = value;
else
properties.Add(name, value);
}
public static T GetValue<T>(this object obj, string name)
{
Dictionary<string, object> properties;
if (ObjectCache.TryGetValue(obj, out properties) && properties.ContainsKey(name))
return (T)properties[name];
else
return default(T);
}
public static object GetValue(this object obj, string name)
{
return obj.GetValue<object>(name);
}
}
At the time of the first implementation, I was not aware of ConditionalWeakTable
so I used a dictionary
. This decision required me to solve the memory management challenges. Because the original solution demonstrates some interesting techniques, I decided to include it into downloadables anyway (AttachedProperties_original.cs).
Also, the original solution can be used under early versions of CLR (except for the GC events). However, if your target platform is .NET 4.0, then you should use the ConditionalWeakTable
based solution (AttachedProperties.cs). It is simpler, better with the memory management and well... did I mention it is simpler?
Limitations
It is important to be aware of the limitations of the presented solution:
ConditionalWeakTable
based solution can only be run on .NET v4.0. ConditionalWeakTable
based solution cannot be extended to support any sort of discovery mechanism. The reason for this is that ConditionalWeakTable
does not support any browsing API as Dictionary
does. - Support for value types as instances to attach the values to is problematic. This is a common limitation for all solutions of this sort.
Points of Interest
The generic Attached Properties is a great "assistance feature" for implementing loosely coupled architecture. They also allow bringing (when required) the stateful nature to the otherwise stateless extensibility model offered by Extension Methods. Which in turn brings Extension Methods up to the level when it is possible to implement what I would call "Safe Multiple Inheritance" in C# . Though it is another topic for discussion...
History