Suggestions on how to avoid disposing of an object twice - c#

I have a a list of disposable items that I am adding to collection that already contains a number of disposable items. I wrap the code in a try...finally block so that if an exception is thrown while I am copying the items from the list to the collection all the objects in the list get disposed of correctly:
private static void LoadMenuItems(ApplicationMenu parent)
{
List<ApplicationMenuItem> items = null;
try
{
items = DataContext.GetMenuItems(parent.Id);
foreach (var item in items)
{
parent.Items.Add(item);
}
items = null;
}
finally
{
if (items != null)
{
foreach (var item in items)
{
item.Dispose();
}
}
}
}
If an exception occurs after adding a number of the objects to the collection, I'll have a situation where the collection contains some disposed objects. Which could give rise to those disposed objects being disposed of again in the following try...catch block:
try
{
// Assume that menu.Items contains some items prior
// to the call to LoadMenuItems.
LoadMenuItems(menu);
}
catch
{
// The Dispose() iterates through menu.Items calling
// Dispose() on each.
menu.Dispose();
}
So what I am looking for possible solutions to stop Dispose() being called twice. I have a solution in mind, but thought I would give it to the community to see if there are any alternatives.

Which could give rise to those disposed objects being disposed of again
Which should be quite alright. The contract for Dispose() is very specific: it should be safe to call it multiple times.
But another way to get rid of it:
Analyzing your logic I would say the finally part is superfluous, maybe the analyzer thinks so too. You really do solve the same problem twice.

Most cases where one might be worried about "accidentally" disposing an object multiple times come about because there is confusion about who "owns" the object in question, and such confusion will likely create other problems in addition to repeated disposal. While one could avoid having the multiple-disposal itself cause problems by having the disposal method use a flag so the second dispose attempt will return harmlessly, doing that without resolving the confusion about IDisposable ownership would not lave the more serious issues unresolved.
The primary scenarios where repeated disposal attempts should not be regarded as indicating broader problems are
Situations where creation of an object fails with a partially-constructed object; while one could probably define policies as to which parts of a partially-constructed object are responsible for cleaning up which other parts, it may be easier to simply have each part that's told to perform an "emergency" cleanup to tell every part it knows about to do likewise if it hasn't already. In most disposal scenarios, confusions regarding object ownership can result in objects being disposed while still in use, but if an object factory fails, that generally implies that no references to the object have been released to anyone who is going to use them.
Situations where disposal of an object which is in use is a legitimate usage scenario with predictable semantics. For example, some data sources have blocking wait methods, and explicitly specify that disposal of the data source while a blocking method is waiting on it will that method to fail without further delay. In some cases, it may well be that yanking the disposable resource out from under the task that's using it is the only way for such a task to become unstuck.
Your scenario seems somewhat like the first one, except that it looks like you'll be disposing of each item after it gets added to parent.Items, which would suggest that you may have muddled issues of object ownership.

Related

WeakReference to IDisposable

I have a class holding a WeakReference pointing to an object implmenting IDisposable. After the object is disposed there is a period of time before it is garbage collected. During that time the WeakReference can still be used. This can result in unexpected behavior because we are now making calls against an object that has been disposed.
Does anyone have a suggested approach for dealing with this, checking the weak reference if the target has been disposed, etc.?
Background:
We have a WinForms application with a controller holding data. Multiple UI controls may be presenting the data at any given time. A form adds and removes the controls (and calls Dispose when it removes them) but is ignorant about what the controls are doing or what data they need. Previously the controls would subscribe to events from the controller to receive notifications when the data was updated. This results in memory leaks. To address this the controller now keeps weak references to the controls, and notifies the ones that are still alive.
Since IDisposable and WeakReference have mutual exclusive semantic you need to choose single one. Combining them is dangerous since after a disposal an object most likely will have inconsistent state which isn't suitable for a reusing. IDisposable implies that you'd like to control the life-cycle of your objects and aren't going to take back. It's like to put something into a dumpster and to say: "I'm done with this thing, now I don't care what happens with that". The WeakReference semantic is like to put something near a dumpster and say: "I'm gonna get back in 30 minutes or so, if noone puts this into this dumpster I'll take it back, but if it's inside I don't care".
Weak references imply that objects referenced by them don't have a long life that's they're good for quick memory reclaim but at the same time they give some chance to be reused so in your case you can rely exclusively on WeakReference<T>:
MyType instance;
if (weakRef.TryGetTarget(out instance))
{
// resurrected, still can use it
}
else
{
// object is collected, the new one should be created
}
Just in case anyone else stumbles across this question they way I resolved it was to make the referenced objects have well defined behavior after being disposed (per the comment by Alexei Levenkov). The easiest way to do this was to ignore function calls after the object had been disposed.
public void OnDataChanged(object model)
{
if (IsDisposed)
{
return;
}
...
// method implementation
}
This was only necessary in the single function being called from the weak reference.
Multi-threading was not an issue as these are UI objects and Invoke and BeginInvoke were already required to make calls modifying them.

Disposing of a collection and then it's contents

I have an object, which contains a collection of other objects. I want to dispose of them all by calling the dispose method on the base object. The dispose method of the collection will clear the collection, but it does not dispose of the individual objects it contains. So I need my base object to loop through and dispose of each child object. The last detail is that I want to make sure that the collection is disposed of before the individual objects it contains.
I am thinking I can get this behavior by using Dispatcher.BeginInvoke and a low priority. First passing it the dispose call for the collection and then looping through and disposing each individual item. (pseudo) code looking something like this:
class Foo
{
FooCollection Children;
void Dispose()
{
//unsubscribe from data model events
CurrentDispatcher.BeginInvoke(Children.Dispose, ApplicationIdle, null);
foreach (Foo Child in Children)
{
CurrentDispatcher.BeginInvoke(Child.Dispose, ApplicationIdle, null);
}
}
}
class FooCollection
{
void Dispose()
{
//unsubscribe from data model events
this.Clear();
}
}
Am I on the right track? Or am I just setting myself up with a race condition? i.e. is it possible that the Dispatcher could get around to calling dispose on the collection before the foreach finishes queing up the dispose calls for the children? Is there a better way to get the desired behavior?
It is probably worth noting that each of these classes exposes events, and hooks handlers into other objects. My primary goal is to make sure all of the handlers get cleaned up gracefully when dispose is called.
EDIT (8/24):
The instances of these classes subscribe to events from objects which generally persist for the life of the application. Because of this, the persistent object maintains a reference to each of these instances. I am trying to make sure that each instance unsubscribes from the persistent object's events when the instance is no longer needed.
EDIT 2 (8/30):
This is part of a view model, which represents a data hierarchy retrieved from a web service. The cost of retrieving the data from the web service is high, so the actual data objects returned are cached, generally for the life of the application. The data objects implement ICollectionChanged and IPropertyChanged, the view model objects in question subscribe to these events. The nature of the application is such that user actions will likely cause the view model to be discarded and recreated several times during a session. The nature of the data is such that there will commonly be several thousand nodes in the hierarchy which have to be represented in the model. The primary concerns, as they relate to this question, are:
1) Making sure that the discarded view model unsubscribes from the events of the underlying data model
2) Making sure it is done in such a way that looping through the full hierarchy of the discarded view model does not noticeably impact the user in working with the new instance of the view model.
I have updated the code example to more accurately represent the situation. So the question remains. Will this code give me the expected result in that the collection, and then all of its children, will be queued up for disposal before any object actually gets disposed, and then some time later the thread will start disposing them when it has idle time? Or is it going to create a race condition where the collection could possibly be disposed of before I have finished looping through it?
Most posters have focused on the fact that the collection gets cleared, in all honesty clearing the collection is probably unnecessary, but it is a bit of a habit, and I consider it to be good housekeeping.
Check this link out, it has a nice explanation of garbage collection with regards to event handlers: What best practices for cleaning up event handler references?
It really depends on how your events line up. If you have objects that receive events from other, more persistent objects, they will stick around to receive those events until the event sources are GCed. This indicates that your dispose method should probably clear the events that you are hooked up to (with the -= operator) if you really need to reclaim this memory (how big are these objects? what's the system you are running this on?) You'll have to call this Dispose method yourself when you're done with the objects...
Clearing the collection is not necessary - just make sure all the references to any objects that you are done with are set to null. When no references exist and no event handlers must be maintained, the GC will collect the object.
I also wouldn't worry about the dispatcher.. unsubscribing from events shouldn't pose a performance issue.
You don't choose when to dispose managed objects - the GC does. You can't dispose a collection before you dispose its children.... because you can't dispose a collection at all! You can only prepare a managed object for garbage collection, and the way to do that is
(1) Clear event handlers and
(2) Get rid of all the references to the objects you are done with.
The GC will take care of the rest.
You won't be able to guarantee the sequence the dispatcher calls dispose. Given that, it'd be better in my opinion to dispose of the individual items in the collection and then call dispose on the collection.
If the collection is cleared prior to the foreach loop completing you could end up with an exception.
I would suggest you a bit another design. Please note, it's not final code, just to show the idea.
class Bar:IDisposable
{
void Dispose()
{
//do some logic
}
}
class BarCollection:List<Bar>,IDisposable
{
void Dispose()
{
foreach(Bar bar in this){
bar.Dispose();
}
}
}
class Foo
{
BarCollection Children;
void Dispose()
{
CurrentDispatcher.BeginInvoke(Children.Dispose, ApplicationIdle, null);
}
}

Object disposal in .Net

Assuming i have the following classes:
Class MainClass
{
private OtherClass1;
MainClass()
{
OtherClass1 = new OtherClass1();
}
void dispose()
{
OtherClass1 = null;
}
}
class OtherClass1
{
private OtherClass2;
OtherClass1()
{
OtherClass2 = new OtherClass2();
}
}
class OtherClass2
{
}
If i instatiate MainClass and later call dispose method, does the OtherClass1 gets garbage collected (later on)? Or do i have first to clear the reference to OtherClass2?
An object will get garbage collected if it has no references, or the references it does have are from objects that themselves don't have references (and so on).
A way of visualising it, is the garbage collector will walk the object reference graph, following all object references, making a note of ones it gets to (still referenced from somewhere). Any it doesn't get to are eligible for garbage collection as if it didn't get to them then they can't possibly be used.
See here for in-depth info (particularly "The Garbage Collection Algorithm"): http://msdn.microsoft.com/en-us/magazine/bb985010.aspx
So yes, it'll be eligible to be GC'd.
Also, if you have a dispose method you really should implement IDisposable.
In the code as provided, you don't have to null anything, you can safely remove your dispose() and all will be well.
If your OtherClass1 and/or OtherClass2 are managed resources, ie they implement the IDisposable interface then your code is not good enough. You then will have to chain the Dispose:
class MainClass : IDisposable
{
private OtherClass1;
MainClass()
{
OtherClass1 = new OtherClass1();
}
public void Dispose()
{
OtherClass1.Dispose();
// OtherClass1 = null; // not needed
}
}
If there is no other reference to it, it may at some time be garbage collected. Note that this is not deterministic, you cannot rely on it being collected in a specific timespan.
In general, you shouldn't worry too much abot this, the GC in .NET can by design handle circular references etc. without any problem. Setting fields to null is usually not required. The Dispose method is usually used to release unmanaged resources, such as database connections etc. in a deterministic fashion; it's not about freeing the memory of the object being disposed.
The best practice is to implement IDisposable interface and implementing the Dispose() method.
At Dispose(), you just release the resources used by your object such as any external resources, COM references, database connections, etc.
In terms of When the object will be garbage collected, it's up to the .NET engine to decide that as they frequently update their disposal algorithm with each release.
In general, when an object is orphan (no variable references it), it will be in the queue to be garbage collected.
You can manually call GC.Collect(); but that's not recommended since it interferes .NET garbage collection mechanism.
The term "Dispose" is a bit of a misnomer, since the Dispose method doesn't delete the targeted object but rather serves a request for the targeted object to do anything that will need to be done before it may be safely abandoned. Essentially, it's a request for the object to put its affairs in order.
The most common situation when a particular object will need to put its affairs in order is when some entities outside of it may be doing something, storing something, refraining from doing something, or otherwise temporarily altering their behavior on its behalf. Note that the entities may be .net objects, other types of OS-recognized objects (GDI handles, etc.), etc. but there's no particular requirement that the entity be any particular kind of thing, nor that they be in the same computer, or even any computer. For an object to puts its affairs in order, outside entities doing, holding, etc. anything on its behalf need to be told that they no longer need to do so. If the entities in question are .net objects that implement IDisposable, the notification would be generally performed by calling their Dispose method.
Note that .net provides a means by which objects can ask to be notified if the system notices that they've been abandoned, and use that as a cue to put their affairs in order. Such notifications may not come in timely fashion, and various factors may cause them to be delayed essentially indefinitely, but the mechanism (called "finalization") is sometimes better than nothing.

Generic function to handle disposing IDisposable objects

I am working on a class that deals with a lot of Sql objects - Connection, Command, DataAdapter, CommandBuilder, etc. There are multiple instances where we have code like this:
if( command != null )
{
command.Dispose();
}
if( dataAdapter != null )
{
dataAdapter.Dispose();
}
etc
I know this is fairly insufficient in terms of duplication, but it has started smelling. The reason why I think it smells is because in some instances the object is also set to null.
if( command != null )
{
command.Dispose();
command = null;
}
I would love to get rid of the duplication if possible. I have come up with this generic method to dispose of an object and set it to null.
private void DisposeObject<TDisposable>( ref TDisposable disposableObject )
where TDisposable : class, IDisposable
{
if( disposableObject != null )
{
disposableObject.Dispose();
disposableObject = null;
}
}
My questions are...
Is this generic function a bad idea?
Is it necessary to set the object to null?
EDIT:
I am aware of the using statement, however I cannot always use it because I have some member variables that need to persist longer than one call. For example the connection and transaction objects.
Thanks!
You should consider if you can use the using statement.
using (SqlCommand command = ...)
{
// ...
}
This ensures that Dispose is called on the command object when control leaves the scope of the using. This has a number of advantages over writing the clean up code yourself as you have done:
It's more concise.
The variable will never be set to null - it is declared and initialized in one statement.
The variable goes out of scope when the object is disposed so you reduce the risk of accidentally trying to access a disposed resource.
It is exception safe.
If you nest using statements then the resources are naturally disposed in the correct order (the reverse order that they were created in).
Is it necessary to set the object to null?
It is not usually necessary to set variables to null when you have finished using them. The important thing is that you call Dispose when you have finished using the resource. If you use the above pattern, it is not only unnecessary to set the variable to null - it will give a compile error:
Cannot assign to 'c' because it is a 'using variable'
One thing to note is that using only works if an object is acquired and disposed in the same method call. You cannot use this pattern is if your resources need to stay alive for the duration of more than one method call. In this case you might want to make your class implement IDisposable and make sure the resources are cleaned up when your Dispose method is called. I this case you will need code like you have written. Setting variables to null is not wrong in this case but it is not important because the garbage collector will clean up the memory correctly anyway. The important thing is to make sure all the resources you own are disposed when your dispose method is called, and you are doing that.
A couple of implementation details:
You should ensure that if your Dispose is called twice that it does not throw an exception. Your utility function handles this case correctly.
You should ensure that the relevant methods on your object raise an ObjectDisposedException if your object has already been disposed.
You should implement IDisposable in the class that owns these fields. See my blog post on the subject. If this doesn't work well, then the class is not following OOP principles, and needs to be refactored.
It is not necessary to set variables to null after disposing them.
If you're objects have a lot of cleanup to do, they might want to track what needs to be deleted in a separate list of disposables, and handle all of them at once. Then at teardown it doesn't need to remember everything that needs to be disposed (nor does it need to check for null, it just looks in the list).
This probably doesn't build, but for expanatory purposes, you could include a RecycleBin in your class. Then the class only needs to dispose the bin.
public class RecycleBin : IDisposable
{
private List<IDisposable> _toDispose = new List<IDisposable>();
public void RememberToDispose(IDisposable disposable)
{
_toDispose.Add(disposable);
}
public void Dispose()
{
foreach(var d in _toDispose)
d.Dispose();
_toDispose.Clear();
}
}
I assume these are fields and not local variables, hence why the using keyword doesn't make sense.
Is this generic function a bad idea?
I think it's a good idea, and I've used a similar function a few times; +1 for making it generic.
Is it necessary to set the object to null?
Technically an object should allow multiple calls to its Dispose method. (For instance, this happens if an object is resurrected during finalization.) In practice, it's up to you whether you trust the authors of these classes or whether you want to code defensively. Personally, I check for null, then set references to null afterwards.
Edit: If this code is inside your own object's Dispose method then failing to set references to null won't leak memory. Instead, it's handy as a defence against double disposal.
I'm going to assume that you are creating the resource in one method, disposing it in another, and using it in one or more others, making the using statement useless for you.
In which case, your method is perfectly fine.
As far the second part of your question ("Is setting it to null necessary?"), the simple answer is "No, but it's not hurting anything".
Most objects hold one resource -- memory, which the Garbage collection deals with freeing, so we don't have to worry about it. Some hold some other resource as well : a file handle, a database connection, etc. For the second category, we must implement IDisposable, to free up that other resource.
Once the Dispose method is called, both categories because the same : they are holding memory. In this case, we can just let the variable go out of scope, dropping the reference to the memory, and allowing to GC to free it eventually -- Or we can force the issue, by setting the variable to null, and explicitly dropping the reference to the memory. We still have to wait until the GC kicks in for the memory to actually be freed, and more than likely the variable will go out of scope anyway, moments after to set it to null, so in the vast majority of cases, it will have no effect at all, but in a few rare cases, it will allow the memory to be freed a few seconds earlier.
However, if your specific case, where you are checking for null to see if you should call Dispose at all, you probably should set it to null, if there is a chance you might call Dispose() twice.
Given that iDisposable does not include any standard way of determining whether an object has been disposed, I like to set things to null when I dispose of them. To be sure, disposing of an object which has already been disposed is harmless, but it's nice to be able to examine an object in a watch window and tell at a glance which fields have been disposed. It's also nice to be able to have code test to ensure that objects which should have been disposed, are (assuming the code adheres to the convention of nulling out variables when disposing of them, and at no other time).
Why don't you use using C# construct? http://msdn.microsoft.com/en-us/library/yh598w02.aspx
Setting to null if not required.
Others have recommended the using construct, which I also recommend. However, I'd like to point out that, even if you do need a utility method, it's completely unnecessary to make it generic in the way that you've done. Simply declare your method to take an IDisposable:
private static void DisposeObject( ref IDisposable disposableObject )
{
if( disposableObject != null )
{
disposableObject.Dispose();
disposableObject = null;
}
}
You never need to set variables to null. The whole point of IDisposable.Dispose is to get the object into a state whereby it can hang around in memory harmlessly until the GC finalises it, so you just "dispose and forget".
I'm curious as to why you think you can't use the using statement. Creating an object in one method and disposing it in a different method is a really big code smell in my book, because you soon lose track of what's open where. Better to refactor the code like this:
using(var xxx = whatever()) {
LotsOfProcessing(xxx);
EvenMoreProcessing(xxx);
NowUseItAgain(xxx);
}
I'm sure there is a standard pattern name for this, but I just call it "destroy everything you create, but nothing else".
I answered this in my question and answer here:
Implementing IDisposable (the Disposable Pattern) as a service (class member)
It implemented a simplistic, reusable component that works for any IDisposable member.

Synchronized IEnumerator<T>

I'm putting together a custom SynchronizedCollection<T> class so that I can have a synchronized Observable collection for my WPF application. The synchronization is provided via a ReaderWriterLockSlim, which, for the most part, has been easy to apply. The case I'm having trouble with is how to provide thread-safe enumeration of the collection. I've created a custom IEnumerator<T> nested class that looks like this:
private class SynchronizedEnumerator : IEnumerator<T>
{
private SynchronizedCollection<T> _collection;
private int _currentIndex;
internal SynchronizedEnumerator(SynchronizedCollection<T> collection)
{
_collection = collection;
_collection._lock.EnterReadLock();
_currentIndex = -1;
}
#region IEnumerator<T> Members
public T Current { get; private set;}
#endregion
#region IDisposable Members
public void Dispose()
{
var collection = _collection;
if (collection != null)
collection._lock.ExitReadLock();
_collection = null;
}
#endregion
#region IEnumerator Members
object System.Collections.IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
var collection = _collection;
if (collection == null)
throw new ObjectDisposedException("SynchronizedEnumerator");
_currentIndex++;
if (_currentIndex >= collection.Count)
{
Current = default(T);
return false;
}
Current = collection[_currentIndex];
return true;
}
public void Reset()
{
if (_collection == null)
throw new ObjectDisposedException("SynchronizedEnumerator");
_currentIndex = -1;
Current = default(T);
}
#endregion
}
My concern, however, is that if the Enumerator is not Disposed, the lock will never be released. In most use cases, this is not a problem, as foreach should properly call Dispose. It could be a problem, however, if a consumer retrieves an explicit Enumerator instance. Is my only option to document the class with a caveat implementer reminding the consumer to call Dispose if using the Enumerator explicitly or is there a way to safely release the lock during finalization? I'm thinking not, since the finalizer doesn't even run on the same thread, but I was curious if there other ways to improve this.
EDIT
After thinking about this a bit and reading the responses (particular thanks to Hans), I've decided this is definitely a bad idea. The biggest issue actually isn't forgetting to Dispose, but rather a leisurely consumer creating deadlock while enumerating. I now only read-lock long enough to get a copy and return the enumerator for the copy.
You are right, that's a problem. The finalizer is useless, it will run far too late to be of any use. The code should have deadlocked heavily before that anyway. Unfortunately, there's no way for you to tell the difference between the a foreach calling your MoveNext/Current members or the client code using them explicitly.
No fix, don't do this. Microsoft didn't do it either, they had plenty of reason to back in .NET 1.x. The only real thread-safe iterator you can make is one that creates a copy of the collection object in the GetEnumerator() method. The iterator getting out of sync with the collection is no joy either though.
This seems far too error-prone to me. It encourages situations in which a lock is implicitly/silently taken out, in a way that is not clear to the reader of the code, and makes it likely that a crucial fact about the interface will be misunderstood.
Usually it's a good idea to duplicate common patterns - represent an enumerable collection with IEnumerable<T> that is disposed when you're done with it - but the added ingredient of taking out a lock makes a big difference, unfortunately.
I'd suggest the ideal approach would be to not offer enumeration on collections shared between threads at all. Try to design the whole system so it isn't needed. Obviously this is going to be a crazy pipe-dream sometimes.
So the next best thing would be to define a context within which an IEnumerable<T> is temporarily available, while the lock exists:
public class SomeCollection<T>
{
// ...
public void EnumerateInLock(Action<IEnumerable<T>> action) ...
// ...
}
That is, when the user of this collection wants to enumerate it, they do this:
someCollection.EnumerateInLock(e =>
{
foreach (var item in e)
{
// blah
}
});
This makes the lifetime of the lock explicitly stated by a scope (represented by the lambda body, working much like a lock statement), and impossible to extend accidentally by forgetting to dispose. It's impossible to abuse this interface.
The implementation of the EnumerateInLock method would be like this:
public void EnumerateInLock(Action<IEnumerable<T>> action)
{
var e = new EnumeratorImpl(this);
try
{
_lock.EnterReadLock();
action(e);
}
finally
{
e.Dispose();
_lock.ExitReadLock();
}
}
Notice how the EnumeratorImpl (which needs no particular locking code of its own) is always disposed before the lock is exited. After disposal, it throws ObjectDisposedException in response to any method call (other than Dispose, which is ignored.)
This means that even if there is an attempt to abuse the interface:
IEnumerable<C> keepForLater = null;
someCollection.EnumerateInLock(e => keepForLater = e);
foreach (var item in keepForLater)
{
// aha!
}
This will always throw, rather than failing mysteriously sometimes based on the timing.
Using a method that accepts a delegate like this is a general technique for managing resource lifetimes commonly used in Lisp and other dynamic languages, and while it is less flexible than implementing IDisposable, that reduced flexibility is often a blessing: it removes the concern over clients "forgetting to dispose".
Update
From your comment, I see that you need to be able to hand a reference to the collection to an existing UI framework, which will therefore expect to be able to use the normal interface to a collection, i.e. directly get an IEnumerable<T> from it and be trusted to clean it up quickly. In which case, why worry? Trust the UI framework to update the UI and dispose the collection rapidly.
Your only other realistic option is simply to make a copy of the collection when the enumerator is requested. That way, the lock only needs to be held when the copy is being made. As soon as it's ready, the lock is released. This may be more efficient if the collections are usually small, so the overhead of the copy is less than the performance saving due to shorter locks.
It's tempting (for about a nanosecond) to suggest that you use a simple rule: if the collection is smaller than some threshold, make the copy, otherwise do it in your original way; choose the implementation dynamically. That way you get the optimum performance - set the threshold (by experiment) such that the copy is cheaper than holding the lock. However, I'd always think twice (or a billion times) about such "clever" ideas in threaded code, because what if there is an abuse of the enumerator somewhere? If you forget to dispose it, you won't see a problem unless it's a large collection... A recipe for hidden bugs. Don't go there!
Another potential drawback with the "expose a copy" approach is that clients will undoubtedly fall under the assumption that if an item is in the collection it is exposed to the world, but as soon as it is removed from the collection it is safely hidden. This will now be wrong! The UI thread will obtain an enumerator, then my background thread will remove the last item from it, and then begin mutating it in the mistaken belief that, because it was removed, no one else can see it.
So the copying approach requires every item on the collection to effectively have its own synchronization, where most coders will assume that they can shortcut this by using the collection's synchronization instead.
I had to do this recently. The way I did it was to abstract it so that there is an inner object (reference) that contains both the actual list/array and the count (and a GetEnumerator() implementation; then I can do lock-free, thread-safe enumeration by having:
public IEnumerator<T> GetEnumerator() { return inner.GetEnumerator();}
The Add etc need to be synchronized, but they change the inner reference (since reference updates are atomic, you don't need to synchronize GetEnumerator()). This then means that any enumerator will return as many items as were there when the enumerator was created.
Of course, it helps that my scenario was simple, and my list was Add only... if you need to support mutate / remove then it is much trickier.
In the must used IDisposable implementation, you create a protected Dispose(bool managed) method which always disposes the unmanaged resources you use. By calling your protected Dispose(false) method from your finalizer, you'll dispose the lock as required. The lock is managed, do you'll only dispose it when Dispose(true) is called, where true means that managed objects need to be disposed. Otherwise, when the public Dispose() is called explicitly, it calls the protected Dispose(true) and also GC.SuppressFinalize(this) to prevent the finalizer from running (because there is nothing to dispose of anymore).
Because you never know when the user is done with the enumerator, you have no other option than documenting that the user has to dispose the object. You might want to propose that the user uses a using(){ ... } construction, which automatically disposes the object when done.

Categories