A question came up on Stack Overflow yesterday which I've had to deal with myself before now. There are times when it's helpful to have one value per type, and that value should be an instance of that type. To express it in pseudo-code, you want an IDictionary<typeof(T), T>
except with T varying across all possible types. Indeed, this came up in Protocol Buffers at least once, I believe.
.NET generics don't have any way of expressing this, and you end up with boxing and a cast. I decided to encapsulate this (in MiscUtil of course, although it's not in a released version yet) so that I could have all the nastiness in a single place, leaving the client code relatively clean. The client code makes calls to generic methods which either take an instance of the type argument or return one. It's a really simple class, but a potentially useful one:
/// Map from types to instances of those types, e.g. int to 10 and
/// string to "hi" within the same dictionary. This cannot be done
/// without casting (and boxing for value types) as .NET cannot
/// represent this relationship with generics in their current form.
/// This class encapsulates the nastiness in a single place.
/// </summary>
public class DictionaryByType
{
private readonly IDictionary<Type, object> dictionary = new Dictionary<Type, object>();
/// <summary>
/// Maps the specified type argument to the given value. If
/// the type argument already has a value within the dictionary,
/// ArgumentException is thrown.
/// </summary>
public void Add<T>(T value)
{
dictionary.Add(typeof(T), value);
}
/// <summary>
/// Maps the specified type argument to the given value. If
/// the type argument already has a value within the dictionary, it
/// is overwritten.
/// </summary>
public void Put<T>(T value)
{
dictionary[typeof(T)] = value;
}
/// <summary>
/// Attempts to fetch a value from the dictionary, throwing a
/// KeyNotFoundException if the specified type argument has no
/// entry in the dictionary.
/// </summary>
public T Get<T>()
{
return (T) dictionary[typeof(T)];
}
/// <summary>
/// Attempts to fetch a value from the dictionary, returning false and
/// setting the output parameter to the default value for T if it
/// fails, or returning true and setting the output parameter to the
/// fetched value if it succeeds.
/// </summary>
public bool TryGet<T>(out T value)
{
object tmp;
if (dictionary.TryGetValue(typeof(T), out tmp))
{
value = (T) tmp;
return true;
}
value = default(T);
return false;
}
}
It doesn't implement any of the common collection interfaces, because it would have to do so in a way which exposed the nastiness. I'm tempted to make it implement IEnumerable<KeyValuePair<Type, object>>
but even that's somewhat unpleasant and unlikely to be useful. Easy to add at a later date if necessary.
(I know the XML documentation leaves something to be desired. One day I'll learn how to really do it properly - currently I fumble around if I'm trying to refer to other types etc within the docs.)
Source Click Here.
No comments:
Post a Comment
Post your comments here: