I thought this would be an interesting pattern. A collection of objects is stored in a manager class. When an object is not needed, it is removed from the collection. If it is needed again, and the GC hasn't disposed of it yet, poll for the instance of the existing object with a weak event. For example:
class Manager
{
//--- Events ---
public event SearchingForInstanceEventHandler SearchingForInstance;
//--- Fields ---
List<OneOfMany> _many = new List<OneOfMany>();
//--- Constructors ---
public Manager()
{
// Create several OneOfMany instances.
_many.Add(new OneOfMany(this, "C:\file1.txt"));
_many.Add(new OneOfMany(this, "C:\file2.txt"));
_many.Add(new OneOfMany(this, "C:\file3.txt"));
// Erase the strong references to one or more OneOfMany instances.
_many.Clear();
// Attempt to find and reuse a previously generated instance, if the GC hasn't reclaimed it.
SearchingForInstanceEventArgs e = new SearchingForInstanceEventArgs("C:\file2.txt");
OnSearchingForInstance(e);
OneOfMany oneOfMany = e.OneOfMany;
// If a previously generated instance does not exist or has already been reclaimed by the GC, create a new one.
if (oneOfMany == null)
oneOfMany = new OneOfMany(this, "C:\file2.txt");
// Store a reference to it while it is needed.
_many.Add(e.OneOfMany);
}
//--- Protected Methods ---
protected virtual void OnSearchingForInstance(SearchingForInstanceEventArgs e)
{
if (SearchingForInstance != null)
SearchingForInstance(this, e);
}
}
class OneOfMany
{
//--- Fields ---
string _key;
Manager _manager;
//--- Constructors ---
public OneOfMany(Manager manager, string key)
{
_manager = manager;
_key = key;
_manager.SearchingForInstance += new SearchingForInstanceEventHandler(_manager_SearchingForInstance);
// INSERT HERE: Long process generating much data (worth reusing if still exists in memory, and worth freeing if not needed)
}
//--- _manager Event Handlers ---
void _manager_SearchingForInstance(object sender, SearchingForInstanceEventArgs e)
{
// If this is the instance the manager is searching for, return a reference to this.
if (e.Key == _key)
e.OneOfMany = this;
}
}
class SearchingForInstanceEventArgs : EventArgs
{
//--- Public Constructors ---
public SearchingForInstanceEventArgs(string key)
{
Key = key;
}
//--- Public Properties ---
public string Key { get; private set; }
public OneOfMany OneOfMany{ get; set; }
}
delegate void SearchingForInstanceEventHandler(object sender, SearchingForInstanceEventArgs e);
This can be used to take advantage of memory the GC hasn't disposed yet, and it can use the GC to automatically manage dependencies for you. For example, if class OneOfMany references other OneOfMany instances (and is dependent on them), you can add new references (i.e. dependencies) by querying for an existing instance of the one you need (with an event), and the GC will automatically remove instances no longer depended on. This is in contrast to storing all instances of OneOfMany in a collection and querying the collection for an existing instance of the dependency needed, and having to walk a dependency tree to manually remove non-rooted dependencies.
However, my concern with this is what if an instance is re-rooted in the GC while the GC is running? Can a GC-Rooted reference of OneOfMany be made between the time GC condemns OneOfMany, and the time it disposes it? If not, can the GC be synchronized with this pattern?
EDIT: If the current method of subscribing to the event is not weak, then replace it with a weak one.