« January 2008 | Main | March 2008 »
February 27, 2008
Disposable value types
Way back when, Ian Griffiths wrote a disposable class called TimedLock that made it easy to use a C# using statement to release the lock. Eric Gunnerson advised him to use a struct instead of a class because a value type avoids a heap allocation. Some time later, Phil Haack verified that the using statement doesn't box the value type in order to call Dispose (as long as IDisposable is implemented explicitly, not implicitly).
Disposable value types do make for a nice optimization in C#, but like many optimizations, they should only be used when performance demands it, because they have a few disadvantages.
Most importantly, there's nothing to keep the Dispose logic from being called twice, perhaps even accidentally. Chris Lyon touches on this when he explains why GCHandle doesn't implement IDisposable. If you make a copy of a disposable value type before you call Dispose, there is no way to avoid executing the same logic if Dispose is called on each copy. This could cause an unmanaged resource to be freed twice, or cleanup code to be executed twice, etc. A class doesn't have this problem because it is only copied by reference; once Dispose is called, it can make sure that subsequent calls to Dispose do nothing.
Another disadvantage of disposable value types is that C++/CLI doesn't support them very well. In C++/CLI, you can't define disposable value types at all; compiler errors prohibit you from defining a destructor or a Dispose method on a value type. You can use a disposable value type defined in C#, but you can't dispose one without boxing it, which causes a heap allocation, thus subverting the purpose of the optimization.
Also, consider this: If the creation or disposal logic of the value type happens to require a heap allocation (creating a delegate, for example), it is unlikely that the optimization will come to much, since the whole point of a disposable struct is to avoid heap allocation.
In summary, I'm not using disposable value types with the reckless abandon that I once did. But if performance measurements ever show that I'm spending too much time allocating a disposable class that's used in this way, I know that I can try making it a struct.
Posted by Ed Ball at 9:50 AM | Comments (1) | TrackBack
February 19, 2008
Disposed objects
There are a few rules about disposed objects. First, calling Dispose on an already disposed object should do nothing. Second, calling any other property or method on an already disposed object should throw an ObjectDisposedException.
Either way, you'll need a way to determine whether an instance of your disposable class has been disposed. The easiest and most obvious way is to have a simple Boolean member that is set to true when the object is disposed.
More often than not, we find that setting a field to null that wouldn't otherwise be null works just as well. Commonly, that field needs to be disposed, so we use a utility method that can be used to dispose a field and set it to null – unless it is already null. Using this method ensures that calling Dispose a second time won't throw a NullReferenceException.
public static class DisposableUtility
{
public static void Dispose<T>(ref T obj) where T : class, IDisposable
{
if (obj != null)
{
obj.Dispose();
obj = null;
}
}
}
An easy way to throw ObjectDisposedException when your object has already been disposed is to define a private (or protected) method that throws the exception if necessary; just call that method at the beginning of every property or method definition of your class (besides Dispose, of course).
sealed class FileReader : IDisposable
{
public FileReader(string strFilePath)
{
m_stream = new FileStream(strFilePath, FileMode.Open);
}
public object ReadNext()
{
VerifyNotDisposed();
object obj = null;
// ...
return obj;
}
public void Dispose()
{
DisposableUtility.Dispose(ref m_stream);
}
private void VerifyNotDisposed()
{
if (m_stream == null)
throw new ObjectDisposedException(GetType().Name);
}
Stream m_stream;
}
Nothing too complicated, but it's easy to forget the two rules of disposed objects. Get in the habit of verifying both rules when you write your unit tests.
Posted by Ed Ball at 8:57 AM | Comments (0) | TrackBack
February 6, 2008
Finding ancestor elements in WPF
Philipp Sumi recently posted a method that uses VisualTreeHelper.GetParent to find an ancestor of a WPF element. The problem with using VisualTreeHelper.GetParent is that it doesn't work with "content elements" like Hyperlink, Run, etc., which means that his TryFindFromPoint method would fail if the mouse cursor was over one of those types of elements. We've had to write our own GetParent method that traverses through content elements:
public static DependencyObject GetParent(DependencyObject obj)
{
if (obj == null)
return null;
ContentElement ce = obj as ContentElement;
if (ce != null)
{
DependencyObject parent = ContentOperations.GetParent(ce);
if (parent != null)
return parent;
FrameworkContentElement fce = ce as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
return VisualTreeHelper.GetParent(obj);
}
We only recently discovered ContentOperations.GetParent; I won't be surprised if there are other "GetParent" methods hiding in WPF that we'll eventually need to support as well.
We also have an ancestor-finding method that avoids recursion:
public static T FindAncestorOrSelf<T>(DependencyObject obj)
where T : DependencyObject
{
while (obj != null)
{
T objTest = obj as T;
if (objTest != null)
return objTest;
obj = GetParent(obj);
}
return null;
}
It's been fun finding so much great WPF content in blogs; I hope the trend continues!
Update: If you want to eliminate the 'parent' and 'fce' temporary variables from GetParent, you can use our IfNotNull extension method and replace that block of code with:
return ContentOperations.GetParent(ce) ??
(ce as FrameworkContentElement).IfNotNull(fce => fce.Parent);
Posted by Ed Ball at 9:23 AM | Comments (2) | TrackBack
February 5, 2008
GetOrAddValue
Jon Skeet recently posted his DictionaryUtility.GetOrCreate method.
Here's our version (sans-parameter validation):
public static TValue GetOrAddValue<TKey, TValue>(
this IDictionary<TKey, TValue> dict, TKey key)
where TValue : new()
{
TValue value;
if (dict.TryGetValue(key, out value))
return value;
value = new TValue();
dict.Add(key, value);
return value;
}
public static TValue GetOrAddValue<TKey, TValue>(
this IDictionary<TKey, TValue> dict, TKey key,
Func<TValue> generator)
{
TValue value;
if (dict.TryGetValue(key, out value))
return value;
value = generator();
dict.Add(key, value);
return value;
}
First note that we have an overload that takes a Func<TValue>. This enables us to call GetOrAddValue even for types that don't support a parameterless constructor. We pass a Func<TValue> rather than a TValue to avoid needlessly creating empty instances when they already exist in the dictionary.
Also, since we're using the looked-up value if it exists, we call TryGetValue instead of checking ContainsKey.
Posted by Jacob Carpenter at 5:23 PM | Comments (0) | TrackBack
February 4, 2008
Another extension method: DisposeAfter
Ed previously blogged about our null-propagating extension method. This extension method is one example of a pattern that I find very interesting:
public static ? InvokeOnSelf<T>(this T self, Func<T, ?> toInvoke)
Another interesting example of this pattern is DisposeAfter:
public static T DisposeAfter<TDisposable, T>(this TDisposable d,
Func<TDisposable, T> fn) where TDisposable : IDisposable
DisposeAfter is an extension method constrained to types which implement IDisposable. It lets you pass in work to be done before calling Dispose, and returns the result of that work.
Why is this useful?
Let's look at a simple case of reading a row of data from an IDataReader:
int id = 0;
string name = null;
using (IDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
id = reader.GetInt32(0);
name = reader.GetString(1);
}
}
// do some work with id and name
This is one case where using an anonymous type instance to encapsulate the read data could be useful. Doing so would enable us to know if data was read; we could simply return null when it wasn't. It would also somewhat simplify the method by reducing the number of local variables.
There's a big problem though: if we declare the anonymous type instance inside the using block, its scope will be constrained. But we cannot declare the anonymous type separately from its initialization.
We want to say something like:
var result; // error CS0818: Implicitly-typed local variables must be initialized
using (IDataReader reader = command.ExecuteReader())
{
if (reader.Read())
result = new { Id = reader.GetInt32(0), Name = reader.GetString(1) };
}
// do some work with result
But that won't compile.
Enter DisposeAfter:
var result = command.ExecuteReader().DisposeAfter(r =>
r.Read() ? new { Id = r.GetInt32(0), Name = r.GetString(1) } : null);
// do some work with result
There, that's nice! Since DisposeAfter is a generic method, the return value is strongly typed. And it doesn't matter to the compiler that we're returning an anonymous type instance; it can still infer the type of result.
Looking at the implementation of DisposeAfter we see that all of the passed in work is still done within a using statement, so objects will properly be disposed (even in the face of troublesome exceptions):
public static T DisposeAfter<TDisposable, T>(this TDisposable d,
Func<TDisposable, T> fn) where TDisposable : IDisposable
{
using (d)
return fn(d);
}
Let me know in the comments if you find this method useful. Hopefully you'll also start to think about other circumstances where passing a delegate (or perhaps an expression tree) to an object via an extension method could be useful.
Posted by Jacob Carpenter at 9:12 AM | Comments (4) | TrackBack
February 1, 2008
The Dispose Pattern
The "Dispose pattern" describes the proper way for a type to implement Dispose and/or Finalize. It applies in different ways to different .NET languages. In C++/CLI, for example, the Dispose pattern is automatically implemented by managed types with destructors and/or finalizers. This post is primarily aimed at C# developers.
The Dispose method of the IDisposable interface is used to free the resources used by an object, which may include unmanaged resources that need to be released, managed resources that need to be disposed, and other miscellaneous cleanup.
The Finalize method of Object (that is, the "finalizer") is similar, but is called automatically after the object is garbage collected, and should only be overridden to release unmanaged resources, since managed resources may already have been finalized and shouldn't be accessed. (In C#, you use the "destructor syntax" to create a finalizer.)
First things first: you don't need a finalizer. Finalizers can only be used to release unmanaged resources. Your clients should be calling Dispose, which will release those resources. If you need to release those resources even if the client forgets to call Dispose, you should wrap each resource in a separate finalizable class derived from SafeHandle or CriticalHandle or CriticalFinalizerObject. Feel free to implement a simple finalizer for debugging purposes, but consider removing it from shipping code.
#if DEBUG
~Program()
{
Console.WriteLine("Program finalized");
}
#endif
If you're really, really sure that you need to ship with a finalizer, be sure to understand and obey all of the rules. Otherwise, with finalizers out of the way, we can discuss a simpler set of rules for implementing the Dispose pattern.
The simplest case is a sealed class whose base class doesn't already implement IDisposable. Just implement IDisposable and do your cleanup in the Dispose method.
public sealed class SimpleSealed : IDisposable
{
public void Dispose()
{
// clean up
}
}
If your class isn't sealed (and its base class doesn't already implement IDisposable), you need to provide the proper mechanism for derived classes to add their own cleanup. Specifically, you need to add a virtual method, also called Dispose, that takes a Boolean argument that indicates whether it is being called from Dispose or from the finalizer. (If a derived class needed a finalizer, it would add one that called Dispose(false). The call to GC.SuppressFinalize would prevent it from being called if the object was already disposed.)
public class Simple : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposing)
{
// clean up
}
}
}
(If your class is abstract, you may be tempted to make Dispose(bool) abstract as well. Unfortunately, that breaks the pattern enough to cause derived classes implemented in C++/CLI to enter an infinite loop when they are disposed.)
If your base class already implements IDisposable, it ideally conforms to the Dispose pattern, which means it has a virtual Dispose(bool) that should be overridden to do cleanup.
public class DerivedFromSimple : Simple
{
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
// clean up
}
}
finally
{
base.Dispose(disposing);
}
}
}
If your base class implements IDisposable but doesn't conform to the Dispose pattern, you should introduce the Dispose pattern for your derived classes.
public class VirtualDispose : IDisposable
{
public virtual void Dispose()
{
// clean up
}
}
public class DerivedFromVirtualDispose : VirtualDispose
{
public sealed override void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
try
{
// clean up
}
finally
{
base.Dispose();
}
}
}
}
(If the Dispose method of the base class isn't virtual, you won't need the "sealed override", but you'll need to reimplement IDisposable. If the Dispose method of the base class is abstract, you won't be able to call "base.Dispose()".)
Finally, if your base class doesn't conform to the Dispose pattern, but your class is sealed, you can keep it simple.
public sealed class SealedFromVirtualDispose : VirtualDispose
{
public override void Dispose()
{
try
{
// clean up
}
finally
{
base.Dispose();
}
}
}
(If the Dispose method of the base class isn't virtual, you won't be able to "override", so you'll have to reimplement IDisposable.)
I'd still like to talk about ensuring that Dispose can be called multiple times, when to throw ObjectDisposedException, the thread safety of this pattern, and disposable value types (structs), but those topics will have to wait for future posts.
Posted by Ed Ball at 9:24 AM | Comments (1) | TrackBack