Monday, March 25, 2013

Sasa.Weak<T> - Typed Weak References

This is the ninth post in my ongoing series covering the abstractions in Sasa. Previous posts:

System.WeakReference is a special runtime class used for collection and finalization of resources that have no other live references. The only problem is that the encapsulated value is of type "object", and so using it requires a lot more casting than it should. More often than not, a WeakReference will only encapsulate a value of one type, so this casting is often superfluous.

Enter Sasa.Weak<T>, which is a struct that wraps WeakReference and provides a typed interface to encapsulated values. As a struct, it does not incur any additional memory allocation costs, and the casting it performs are operations you would likely have to do anyway, so the overhead of the typed interface is virtually nil. Sasa.Weak<T> is available in the core Sasa.dll assembly.

Sasa.Weak<T>.HasValue

Sasa.Weak<T>.HasValue has the same purpose as Weak<T>.IsAlive, but fulfills the requirements to satisfy the IResolvable<T> interface:

string original = new string("hello world!".ToCharArray());
Weak<string> foo = original;
Console.WriteLine(foo.HasValue);

// lose all live references and run collection
original = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(foo.HasValue);

// outputs:
// true
// false

Sasa.Weak<T>.IsAlive

Sasa.Weak<T>.IsAlive checks whether the target of the weak reference has yet to be collected:

string original = new string("hello world!".ToCharArray());
Weak<string> foo = original;
Console.WriteLine(foo.IsAlive);

// lose all live references and run collection
original = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(foo.IsAlive);

// outputs:
// true
// false

Sasa.Weak<T>.Value

Sasa.Weak<T>.Value obtains the current value encapsulated in the weak reference, or null if the object has been collected:

string original = new string("hello world!".ToCharArray());
Weak<string> foo = original;
Console.WriteLine(foo.Value);

// lose all live references and run collection
original = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(foo.Value ?? "<null>");

// outputs:
// hello world!
// <null>

The Value property also fulfills the requirements for the IValue<T> interface.

Sasa.Weak<T>.TryGetValue

Sasa.Weak<T>.TryGetValue checks whether a weak reference to a target is alive, and returns a live reference to that target in one step:

string original = new string("hello world!".ToCharArray());
Weak<string> foo = original;
Console.WriteLine(foo.TryGetValue(out original));
Console.WriteLine(original);

// lose all live references and run collection
original = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(foo.TryGetValue(out original));
Console.WriteLine(original ?? "<null>");

// outputs:
// true
// hello world!
// false
// <null>

This method also satisfies the requirements for the IVolatile<T> interface.

2 comments:

Rodrigo Kumpera said...

Since version 4.5m there's a WeakReference variant that offers the same interface.

Sandro Magi said...

Good to hear. Sasa is written for .NET 3.5, and I've had Weak for over 5 years now.