More Weak Help

Every once in a while something happens in conversation, and someone says something that may prove useful.

The other day was one of those days, a bunch of us WPF Disciples were having a chat with Glenn Block (Patterns & Practices / MEF), about some mad arse scheme he had for getting rid of INotifyPropertyChanged smell.

Anyway the result was that Glenn told us about a rather useful class in the .NET 4.0 base class library that made it easy to work with a collection of weak reference objects.

Now I don’t know how many of you are familiar with WeakReference and or WeakEvents, or even the idea of Weak anything. It’s kind of a hot topic these days to allow object references to be maintained without a strong reference to an object, thus allowing objects to be garbage collected. Which is kind of a nice thing.

You may have come across this in PRISMs (CAL) EventAggregator, or Josh/Marlons Mediator messager, which I am using in my own WPF framework Cinch

Anyway the long and short of it is sometimes it is advantageous to use WeakReferences instead of strong ones.

Now most of us have hand crafted our own solutions around WeakReference, but after Glenns little chat the other day he mentioned a new .NET 4.0 class called ConditionalWeakTable<TKey,TValue>

Here is what MSDN says about this handy little class:

Although the ConditionalWeakTable(TKey, TValue) class holds a collection of key/value pairs, it is best thought of as a table rather than a dictionary object. TheConditionalWeakTable(TKey, TValue) class differs from a dictionary in several ways:

  • It does not persist keys. That is, a key is not kept alive only because it is a member of the collection.

  • It does not include all the methods (such as GetEnumerator or Contains) that a dictionary typically has.

  • It does not implement the IDictionary(TKey, TValue) interface.

The ConditionalWeakTable(TKey, TValue) class differs from other collection objects in its management of the object lifetime of keys stored in the collection. Ordinarily, when an object is stored in a collection, its lifetime lasts until it is removed (and there are no additional references to the object) or until the collection object itself is destroyed. However, in the ConditionalWeakTable(TKey, TValue) class, adding a key/value pair to the table does not ensure that the key will persist, even if it can be reached directly from a value stored in the table (for example, if the table contains one key, A, with a value V1, and a second key, B, with a value P2 that contains a reference to A). Instead,ConditionalWeakTable(TKey, TValue) automatically removes the key/value entry as soon as no other references to a key exist outside the table.

That’s quite cool, so we now have a weak dictionary effectively. Here is small example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using System.Threading;

namespace ConsoleApplication2
{
    //Dummy class
    public class HighSchoolClass
    {
        public String Name { get; private set; }

        public HighSchoolClass(String name)
        {
            this.Name = name;
        }
    }

    //Shows how to use the ConditionalWeakTable<TKey,TValue>
    class Program
    {
        static void Main(string[] args)
        {
            //create a ConditionalWeakTable
            ConditionalWeakTable<HighSchoolClass, 
                    List<Int32>> classScores =
                new ConditionalWeakTable<
                    HighSchoolClass, List<Int32>>();

            //initialise some objects to 
            //store in the ConditionalWeakTable
            HighSchoolClass h1 = 
                new HighSchoolClass("Class1");
            HighSchoolClass h2 = 
                new HighSchoolClass("Class2");

            classScores.Add(h1, 
                new List<Int32> { 10, 20, 30 });
            classScores.Add(h2, 
                new List<Int32> { 11, 21, 31 });




            List<Int32> h1List;

            //see if classScores contains h1 : It should
            if (classScores.TryGetValue(h1, out h1List))
                Console.WriteLine(
                    "classScores contains h1 scores");

            PrintList(h1List);

            //set original h1 to null
            h1 = null;

            //collect it
            GC.Collect();

            // Reinstantiate h1 to new instance
            h1 = new HighSchoolClass("Class3");

            //see if classScores contains h1 : It shouldn't
            if (classScores.TryGetValue(h1, out h1List))
                Console.WriteLine(
                    "classScores contains h1 scores");
            else
                Console.WriteLine(
                    "classScores no longer contains h1 scores");

            PrintList(h1List);

            //Add h1 again using new scores
            classScores.Add(h1, 
                new List<Int32> { 100, 200, 300 });

            //see if classScores contains h1 : It should
            if (classScores.TryGetValue(h1, out h1List))
                Console.WriteLine(
                    "classScores contains h1 scores");
            else
                Console.WriteLine(
                    "classScores no longer contains h1 scores");

            PrintList(h1List);


            Console.ReadLine();
        }

        private static void PrintList(List<Int32> h1List)
        {
            if (h1List == null)
            {
                Console.WriteLine("List is null");
            }
            else
            {

                //print h1 scores
                foreach (var score in h1List)
                {
                    Console.WriteLine(
                        String.Format(
                            "Score is : {0}", score));
                }
            }
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

And here is what we get as output when we run this little demo project.

WeakTable[2]

That is quite a handy class, don’t believe me. Try and write your own messager and tell me different.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Advertisements

4 thoughts on “More Weak Help

  1. Michael Chandler says:

    Nice. Attaching and forwarding on PropertyChanged changed events generated by sub-ViewModels can be tedious, and annoying to cleanup (especially when using lambdas). Hopefully this can help.

  2. Richard says:

    The example isn’t particularly informative. When you set h1 to a new object it won’t be equal to the original key, so even a standard generic Dictionary would show the same behaviour.

    To make this meaningful, you need to override the GetHashCode and Equals methods on the HighSchoolClass so that instances with the same name are equal, and re-create h1 with the same name.

  3. Adam says:

    “…a bunch of us WPF Disciples were having a chat with Glenn Block…”
    Wouldn’t i like to be a fly on the wall 🙂

    How’d you feel about letting curious ones just listen in?
    🙂

    either way, if you could get a nickel for every “Thank You” you get, I’m certain you’d be a wealthy man.
    (still coding but just wealthier )
    Here’s another for the pile then Thanks for all your posts, haven’t read one yet that I wish I hadn’t

  4. Rory says:

    Nice one.

    I worked on a project where we couldn’t go near weaklings.

    As to much garbage collection would impeded the real-time low-latency nature of the app

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: