Click here to Skip to main content
15,880,725 members
Articles / Programming Languages / XML

Extension Properties

Rate me:
Please Sign up or sign in to vote.
4.89/5 (9 votes)
27 Mar 2015CPOL4 min read 21.2K   246   10   1
Another solution for Extension properties in .NET

Introduction

Extension methods have been around for quite a long time in C# and .NET now. This allows a type to be extended with new methods without changing the type itself. Most frequent use of this is probably the LINQ extension methods for collections, for instance Where. When we have the possibility to extend a type with methods, developers naturally start to ask for the possibility to extend types with new properties as well, for instance to store data associated with an object:

C#
myInstance.MyExtensionId = 123; // Syntax not yet supported

However, this syntax is not supported yet in C# or VB. Neverless, various solutions for getting the functionality for extension properties have been proposed, for instance, here at The Code Project (see References below). In this article, I present my own solution which has the following features:

  • lightweight implementation (simple wrapper around ConditionalWeakTable)
  • not using string as keys for property names, increasing and reducing risk of run-time time errors due to typos and after refactory
  • type-safe using generics, both for property type and in the selection of what types that can be extended.
  • can store value types (but cannot extend value types)
  • property change notification (optional)
  • support for read-only /  lazy-initialized extension properties
  • basic one-way XAML/WPF binding support

Using the Code

Let us directly start with an example how to declare and use an extension property:

C#
// Declare the extension property once
static readonly ExtensionProperty<object, int> Id = new ExtensionProperty<object,int>();

// ... and then use it like this:
MyObject testInstance = new MyObject();
testInstance.Set(Id, 123);

int id = testInstance.Get(Id);

Above, we first declare a strongly typed extension property called Id of type int. In this case, we specify that it can be used for any instance derived from Object. Then, we set it and retrieve it for an object using the extension methods. If you compare this with an attached property, you can see some similarities.   

In contrast to many other solutions for Extension properties I have seen, we do not use any literal strings to specify the name of the extension property. This is refactoring (renaming) friendly and reduces the risk of difficult to find problems due to typos.

We cannot get closer to the real property syntax than this. However, we can actually write it even more compactly by using the indexer syntax:

Id[testInstance] = 123;
var id = Id[testInstance];

Here, it becomes clear that ExtensionProperty is similar to an ordinary dictionary. However, even though this is stored in a static field only weak references are held to the entries, so that memory can be reclaimed during garbage collection when there are no other reference to the entries.

Change Notification

If you want to get notified whenever the value of an extension property changes, you can register a handler for the Changed event:

C#
Id.Changed += OnIdChanged;
...
private static void OnIdChanged(object instance, EventArgs arg)
{
     Debug.Print(String.Format("Id for {0} changed to {1}", instance, Id[instance]);
}

Lazy-Initialized Extension Properties

If you want to have an extension property generated for you when it is requested, you can use the LazyProperty. This can be useful if you want to have a view-model, ICommand or something else associated with another object.

C#
public static readonly LazyProperty<MyModel, MyViewModel> MyViewModelProperty = 
                new LazyProperty<MyMode, MyViewModel>(model => new MyViewModel(model));

...

// Get viewmodel for a model object (created first time requested, 
// and garbage collected when not referred to anymore)

var viewModel = model.Get(ViewModelProperty); 

Caution: Although extension properties seem convenient, overuse may hurt performance. As long as you only have a small amount of data to manage, this might not be a problem.

Usage in WPF/XAML Binding

To allow simple use in WPF/XAML binding scenarios, I show in the attached source code how to extend ExtensionProperty to be used as a value-converter returning the value for the extension properties. This allows us to use it like this:

XML
<DataTemplate DataType="{x:Type models:MyModel}" >

   <ContentControl DataContext=
      "{Binding Converter={x:Static viewModels:ViewModels.MyViewModelProperty}}" >

       <TextBlock Text={Binding FullName} />  <!-- FullName is a property of the view model -->

   </ContentControl>

</DataTemplate>

Points of Interest

Simple Basic Implementation

The code-snippet below show the basic simple implementation of ExtensionProperty. In the attached source code, you can see that we added some more methods and a base class to derive LazyProperty from, but in many scenarios, the implementation shown here should be sufficient:

C#
public class ExtensionProperty<TInstance, TProperty>  where TInstance : clas
{
    private readonly ConditionalWeakTable<TInstance, object> values = 
                                 new ConditionalWeakTable<TInstance, object>();
    
    public TProperty this[TInstance instance] {
        get {
            object value;
            if (values.TryGetValue(instance, out value) == false) {
                return default(TProperty);
            }
            return (TProperty)value;
        }
        set {
           lock (values) { // consider removing lock if you never going to use 
                           // if from multiple concurrent threads.
               values.Remove(instance);
               values.Add(instance, value);
           }
           // Change noticification comes here.
       }
    }
}

Memory Leakage?

The use of ConditionalWeakTable guarantees that the value stored in the extension property is garbage collected when there are no references left to the value or to the owner object (key). 

As pointed out in some forum, even if the value and key has been collected, the ConditionalWeakTable still may not reclaim all the memory allocated for its internal storage tables. So for instance, if you have attached 100,000 extension property values and then have removed all references, the ConditionalWeakTable still seems to keep a internal storage with room for at least the same number of entries. This memory is reused if more new extension property values are set, but all memory is not reclaimed until the application (domain) is unloaded. In general, this shall not be a problem. For those who care, I added a ClearAll method to the extension property classes that clears all property values and releases the reference to the ConditionalWeakTable so that all memory for that can be reclaimed.

References

I have not exhaustedly searched for other solutions, but here are some other implementations of Extension Properties:

  1. C# Easy Extension Properties by Moises Barba
  2. "Extension Properties" Revised by Oleg Shio
  3. Connected Properties on CodePlex

History

  • 27th March, 2015 - Initial version published
     

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Sweden Sweden
Henrik Jonsson is a Microsoft Professional Certified Windows Developer (MCPD) that currently works as an IT consultant in Västerås, Sweden.

Henrik has worked in several small and large software development projects in various roles such as architect, developer, CM and tester.

He regularly reads The Code Project articles to keep updated about .NET development and get new ideas. He has contributed with articles presenting some useful libraries for Undo/Redo, Dynamic Linq Sorting and a Silverlight 5 MultiBinding solution.

Comments and Discussions

 
GeneralMy vote of 5 Pin
mbarbac28-Mar-15 6:03
mbarbac28-Mar-15 6:03 

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.