Introduction
I have some code which I can simplify to,
public abstract class Source
{
public string Name;
public Uri Uri;
}
Dictionary<Uri,Source> sourceCollection = new Dictionary<Uri,Source>();
The dictionary is used to synchronise the GUI in terms of adding and deleting sub-classed TreeNode.
if (! sourceCollection.ContainsKey(node.Source.Uri))
node.TreeView.Delete(node);
…
if (!sourceCollection.ContainsKey(uri))
{
Source source = new Source(uri);
sourceCollection.Add(source);
treeView.Add(new SourceNode(source));
}
However I’ve realised that I need to abstract this at a lower level
public abstract class Source
{
public string Name;
}
public abstract class WebSource : Source
{
public Uri Uri;
}
Which of course means that the Dictionary can no longer be used as the unique ref is no only in the derived class not the base, and so instead I moved to a flat list with a search function
List<Source> sourceCollection = new List<Source>();
…
if (! sourceCollection.Contains(node.Source))
node.TreeView.Delete(node);
…
if (!sourceCollection.ContainsKey(uri)) //Whoops
{
…
}
Predicate<T> to the rescue
Fortunately the generic List<T> has a method List<T>.Find(Predicate<T>) where Predicate<T> is a delegate of type bool (T) target or in a more readable example
Source found = sourceCollection.Find(new Predicate<Source>UriPredicate);
if (found == null)
{
Source source = new Source(uri);
sourceCollection.Add(source);
treeView.Add(new SourceNode(source));
}
…
private bool UriPredicate(Source searchMe)
{
if(searchMe is WebSource)
return ((WebSource)searchMe.Uri == uri;
return false;
}
Did we just simplify something? Actually yes. The uri is assumed to be class variable, and that makes the Find(Predicate<T>) non re-entrant, or in English, it will break if ever it is used by more than one thread at the same time.
Making the Predicate<T> re-entrant
This took some thinking about. You can’t just subclass the Predicate, although that would have been elegant
public class UriPredicate : Predicate<Source>
Now the original point of this article was to ask for help as to fix this problem. Fortunately the light bulb flickered on while writing this down (doesn’t it always).
Where was I? Oh yes. You can’t just subclass the Predicate, you can however have it as an argument to another class
public class UriSearcher
{
public Uri SearchUri;
public UriSearcher (Uri uri)
{
SearchUri = uri;
}
private bool UriPredicate(Source searchMe)
{
if(searchMe is StreamSource)
return (((StreamSource)searchMe).Uri == SearchUri);
return false;
}
}
…
UriSearcher searcher = new UriSearcher(uri);
Source found = sourceCollection.Find(new Predicate<Source>searcher.UriPredicate);
Note. This is all theoretical at the moment. I only have VS2005 on my laptop not my desktop. I just needed to get it out of my head once I saw the light.
Update: It works
. I’m now using this code.