tags:

views:

108

answers:

2
+5  Q: 

Type Dictionary?

Has anybody heard of a "type dictionary" that uses types as keys and supports inheritance?

In my application I'd like to have a dictionary from types to functions, sort of like this:

Dictionary<Type, Func<object, object>> Transformers;

The idea is that it would be used to transform an object in some fashion based on its type:

// Transform an object 'obj'
object result = Transformers[obj.GetType()](obj)

An ordinary dictionary has the disadvantage that the type must match exactly. So if I've written a transformer for IList<T>, there's no use putting it in the Transformers dictionary because no object has the type IList<T> (only T[], List<T>, etc.) In other words, if obj is a List<T>, the transformer for IList<T> will not be found by a lookup in an ordinary dictionary.

Assuming there is no such thing as a TypeDictionary<TValue>, I might consider writing one if it isn't too hard. Any ideas how it could be accomplished?

+2  A: 

You should be able to use a dictionary with a custom comparer that uses Type.IsAssignableFrom to compare the keys.

Update: As Qwertie pointed out, this doesn't work because you can't implement a repeatable hash code calculation based on a type, its interfaces, and ancestor classes. His answer provides a possible solution by repeatedly doing hash table look ups for the type, interfaces, and ancestor classes until it finds a match.

The only problem with that solution is that you don't have any way to specify which match to take when there are multiple matches. If you need that flexibility and control, I suggest you consider the chain-of-responsibility design pattern. Each transformer could be a link in the chain, and it's responsible for determining whether it can be applied to the object. If not, it passes the request to the next link. The order of transformers in the chain determines priority. You lose the speed of a hash table, but you were losing some of that speed anyway because of multiple look ups.

Don Kirkby
That wouldn't work. Which hash code would the IEqualityComparer return for a class B that derives from A and implements IA and IB? Also, keep in mind that the Dictionary should be able to hold keys for `List<T>`, `IList<T>` and `object` simultaneously.
Qwertie
You're right, I didn't think that through.
Don Kirkby
@Qwertie in that case, what your dictionary would be returning if there were several matches in the dictionary for a type? Should it return all occurrences, only the most specialized, etc.?
Yann Schwartz
Note that the problem exists only in the case of interfaces (for base classes it's obvious: return the most specialized available type). My TypeDictionary could be improved by using a more deterministic rule: search for base classes first, and if none are found, find the most specialized applicable interface, e.g. if there are handlers for IEnumerable and IList, and we are searching for `List<T>`, obviously the handler for IList, not IEnumerable, should be returned. This would deal with the most common (but not all) sources of ambiguity.
Qwertie
I'm not sure how the chain-of-responsibility design pattern applies in this case. I don't like the pattern anyway, at least not when the links of the chain can be written by different people, as I don't trust every link in the chain to remember to call the next handler in the chain.
Qwertie
I clarified the chain of responsibility a bit. Each transformer becomes a link in the chain. It's up to you whether you make the calling code implement the entire link class or just pass in a function and a type to be held by your own link class in the chain. By the way, if the ambiguity isn't a problem, I think Qwertie's suggestion is simpler.
Don Kirkby
+1  A: 

It occurs to me that the dictionary setter doesn't have different semantics from an ordinary dictionary, so one approach is to use a standard dictionary with specialized lookup:

public class TypeDictionary<TValue> : Dictionary<Type, TValue>
{
 public new TValue this[Type key]
 {
  get {
   TValue value;
   if (TryGetValue(key, out value))
    return value;
   throw new KeyNotFoundException("Not found: " + key.Name);
  }
 }
 public new bool TryGetValue(Type key, out TValue value)
 {
  if (base.TryGetValue(key, out value))
   return true;

  Type[] interfaces = key.GetInterfaces();
  for (int i = 0; i < interfaces.Length; i++)
   if (base.TryGetValue(interfaces[i], out value))
    return true;

  Type @base = key.BaseType;
  if (@base != null && TryGetValue(@base, out value))
   return true;

  return false;
 }
}

Note that if a class B derives from class A and interfaces IA and IB, and there is a value assigned to each of those types, it's ambiguous: should the value for A, IA, or IB be returned? The above implementation chooses the first interface it finds, and only if no interfaces are found, does it look for the base class.

I have no idea how good the performance of this dictionary is. If GetInterfaces() or the BaseType property is slow, it'll make lookup performance pretty bad (whenever the exact type you request is not in the dictionary).

Qwertie