Introduction

We all know that there are no extension properties. Extension methods, yes - but no extension properties. If this has already been a problem to you, this
article might be a solution.

Of course, you should prefer the "clean" way and extend your types if you need new properties. But that is not always possible - there are sealed
classes within the .NET Framework or within third party assemblies. Or it just seems to be an overhead to extend a type for using that property in only one
case. To make it short, I will not discuss the pros and cons about extension properties - you can use them just as you like.

But I will give you an example: I had to implement a module where all kinds of types came in - maybe once, maybe twice, do not know how often. But I had to recognize an instance the second
time it came in. So I wanted to add a "ticket" to any instance. Something as following would be nice:

Of course, without theIncomingReference having implemented "TicketID" or "TicketTime" as a property!

Just in case I could benefit from others work, I searched the internet for extension properties. No direct support was found. I came across
Mixin. But that was not what I was looking for. You will have to use interfaces and factory methods - not as generic
as I wanted it to be. There was really no solution for me. So I did it my way (good old Franky) and came up with the UniversalTypeExtender.

This is it (good old Michael) - working on all reference types without setting up anything - no interfaces, no inheritance, no factory!

Interested in how it works?

Overview

Again, there is no support for extension properties out of the box. So we will have to emulate this. This is done by "binding" a reference
of a type (e.g., Ticket) to the extended object (e.g., theIncomingReference). This "binding" is stored and it is used again on
every request on the extended object. So, actually you work with properties of the "binding" object instead with properties of the extended object.
The trick is that you always get the same "binding" object instance for the same extended object. Sounds easy? Yes, but of course there are a few
things to take care of - like garbage collection. We will have a look at this, soon. But first, a class diagram showing the components of the UniversalTypeExtender:

You can see the "binding" object in the class diagram. It is called RepositoryItem and comes as a nested class within its related
ExtensionRepository. A RepositoryItem holds a reference to the extended object and the extensions of this object.
The ExtensionRepository keeps track of these RepositoryItems. The UniversalTypeExtender itself provides only one
extension method - Extended<T>(). This method returns an Extender for the object to extend. The Extender in turn
manages the different ways of extending an object - for now, Dynamically() and With<TE>(). Dynamically() returns
a real dynamic object (DynamicPropertyExtension) - that is why you can call any property without setting up anything else.
With<TE>() returns an instance of the specified type - that gives you type safe coding.

So, that was a short overview. Let's have a deeper look at each of these components.

UniversalTypeExtender

As already mentioned, all you have to do in order to extend an object is to call the extension method Extended<T>(). That was really
obvious because extension methods are the only way to add our own functionality to any type without touching the code. By using the generic type T,
we can control that extensions only work on reference types - that means classes. It would be problematical on value types because they are
mostly given by value. But the concept behind the extender needs references as you can see later on. Extended<T>() returns an
Extender which gets a reference to the extended object. So the code looks really simple:

As you can see, the real work is delegated to the repository. For that, the With<TE> forces TE to provide a parameterless
constructor because the repository has to create a new instance of TE on its first request. Also, you can see that Dynamically()
just calls With<TE> with a predefined type - DynamicPropertyExtension, at which we will come back later on.

You might wonder why I introduced the Extender instead of just implementing proper extension methods on the
UniversalTypeExtender. I wanted the extension method Extended() to be limited to reference types. So I had to define
Extended<T>() with T being limited to classes. You do not have to think about T - just type
Extended(). But if I had to define TE (from With<TE>()), too - like Extended<T, TE>() - you
would have to specify T on any call. I did not like that, that's all Consider the Extender to be part of a tiny fluent interface.

But let's continue with the ExtensionRepository...

ExtensionRepository

The repository holds a dictionary of RepositoryItems. As you have seen above, the Extender calls GetExtension<T>():

Locking is used because the repository has to make sure that a call gets a "safe" view of the item dictionary. Imagine, a call would create a new item
because the requested item is not there, yet. And at the same time - before the new item is added to the dictionary - another call would do the same.
That would end up in two extension items for the same object! But that would bypass our concept - so we lock.

GetOrNew looks for an item which belongs to the requested instance. If no item was found, it is created. Otherwise it is returned.

Garbage Collection

Before we will inspect the RepositoryItem, we will have a look at garbage collection. This is the .NET way of freeing the memory of unused objects.
If you have not heard about it before, you can get more information here.
The important thing to know is that it removes "unused" objects. That means objects which are not referred any more. Now, have a look at our repository again: all created items
are stored in the inner item dictionary. That means, this dictionary is getting bigger and bigger. Because all these items are referred in one way - at least from the dictionary
itself - there is no automatic garbage collection from the framework. So we will have to take care of this on our own. And of course, we do! If you dig into
the ExtensionRepository's code, you will notice that there is a timer which looks after garbage from time to time:

This method asks every item if it could be collected (how the item itself knows about this is described soon) and removes it, if so. Again, watch out for
locking! You can figure out when the timer starts and stops on your own. You can define the interval for garbage collection by setting
the CollectGarbageIntervalInMilliseconds property of the UniversalTypeExtender. Its default is 15.000 milliseconds.

Garbage collection seems to be just a tiny part of this article, but it really is an important part of the UniversalTypeExtender.

RepositoryItem

Now let's inspect the RepositoryItem. It holds a reference to an extended object and references to all extensions requested for this object. As you can see,
the reference to the extended object is stored as a WeakReference:

If it would be stored as a "normal" field - that means as a strong reference - it would never be collected by the garbage collection, as mentioned
above. But WeakReferences give you the ability to hold a reference by allowing it to be collected if there is no other strong reference to it anymore.
So, you can check if a reference is still alive:

RepositoryItemTargetKey

The RepositoryItemTargetKey is used as key for the inner dictionary of the repository. It is necessary for generating a proper key for a weak reference, which
is a fundamental thing for the UniversalTypeExtender. In my first version, there was a simple list holding the items.
Then, Mike Marynowski pointed me to the dictionary and gave me an idea of how to use
it for weak references. That gave my implementation an enormous performance boost. Thanks, Mike! To make it short, the RepositoryItemTargetKey delegates
GetHashCode and the Equals method to the weak reference. For more details, I recommend to look into the code and to read Mike's comments below this article.

DynamicPropertyExtension

As shown at the beginning, you can extend an object by calling theIncomingReference.Extended().Dynamically(). Then, you can type any property name as you
want and set it to any value you like. Is it a miracle (good old Freddy)? If you know the
DynamicObject from .NET 4, you
already know the answer, which - of course - is: no! The DynamicObject lets you implement dynamic property access. There are special methods you can override,
building the getter and setter of your properties. In this case, the values are stored within a private list - of course with their property names:

You should be aware of just working with an object type here - not very type safe, indeed! There is support for using an index as getter and setter, too.
But this is nearly the same. So, just look into the source, if you like. That's it! You made it through the whole story!

Using the code

The code makes use of Code Contracts and
FluentAssertions for testing. So you will have to install them - both for free - in order to compile and
test the source code. But the final DLL can be found in the bin folder - ready to use. Just reference it and distribute it with your final binaries.
The UniversalTypeExtender comes as an extension method. You will have to reference the TB.ComponentModel namespace in order to use it.

Limitations

As pointed out, it just works for reference types.

Extensions you want to use by the With<TE>() method will have to implement a parameterless default constructor.

As you have seen, the objects are not touched in any way. The extensions are just stored within a static list. That means
that, e.g., serialization will not work. So, putting an extended object into the ViewState of an ASPX page - in hope
of getting the extensions back on a postback - will not work, of course. Whereas storing an object in the Session should do fine.

Performance

Just for interest, I did a little setup for testing the performance. This test was done on a Core Duo 2.67 GHz with 3 GB RAM. During the test, 10,000 objects
were created and each of them was extended. Then these 10,000 objects were looped again and the extension was read for each. The garbage collection of the
UniversalTypeExtender was called during the test. The average duration of the test was about 0.8 seconds - for 20,000 calls total! Just to compare: my first
implementation - working with a list instead of a dictionary - took 8.5 seconds for the same test.

Conclusion

If something is not supported by the framework, you will have to do it on your own .

Points of interest

While using WeakReferences, I had to handle them in my unit tests. That means, I had to control that they were collected by garbage collection so that I could
verify some actions within my code. Normally, you could not - and you should not! - control the time of collecting by yourself. You can trust the runtime - it knows best
when to do this. But for testing, you will have to control it. Therefore, you can call GC.Collect() as you can see in some of my tests.

Sacha Barbar pointed me
to the ConditionalWeakTable class which is a kind of dictionary and implements the functionality
I needed. This means, storing objects as weak references and garbage collecting of these. This seems to be the best solution and maybe I will change it in future.
Thanks to Sacha for this great information.

Comments and Discussions

I have seen lots of conversations about the normal way to add extension properties (just over the last couple of weeks actually, I am lucky enough to see some of these internal Microsoft mail), and from those talks/emails the common method appears to be by using ConditionalWeakTable.

I have knocked up a small demo for you, took like 5 mins, so not that elegent, but you could build on this, and it already deals with direct lookup, weak references, etc etc

I'm really happy, that YOU took the time to have a look at it! Thank you very much for your snippet.
I'll have to admit that the ConditionalWeakTable seems to be something I should have come over during my research. But I did not. However, that's what I like on codeproject - there is always someone with a great tip. At this time, I'm not sure if I'll change my implementation - but I like getting new input for future projects - so your comments are always more than welcome!

"Still great job for getting there by your self, well done"
Nice, that you end up with an ego-boost, after all