Wednesday, April 28, 2010

KeyedCollection

System.Collections.ObjectModel.KeyedCollection is an interesting class, but with one drawback: it serializes in XNA's IntermediateSerializer, but doesn't deserialize. There're ways to write a custom serializer for this class, but I chose a little bit different route - making my own that doesn't need a custom serializer.

Advantages

  • Different methods can be exposed between the pipeline and runtime versions. This can help make the collection immutable if desired.
  • Hides methods that may not be needed at runtime like adding and removing elements.
  • Provides regular for loop access for speed.

Disadvantages

  • Extra complexity of a new class
  • Have to re-implement any methods

Code

This is an abstract base class that serves solely as a container, provides an iterator, and a method of accessing by key.

abstract class KeyedCollection<T> : IEnumerable<T>
{
    #region Internal Data

    [ContentSerializer]
    private List<T> _collection;

    /// <summary>
    /// Keyed lookup: <Key, Collection Index>
    /// </summary>
    [ContentSerializer]
    private Dictionary<int, int> _lookup;

    #endregion;

    #region Properties

    /// <summary>
    /// Indexer
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    [ContentSerializerIgnore]
    public T this[int index]
    {
        get
        {
            return _collection[_lookup[index]];
        }
    }

    /// <summary>
    /// Number of elements in the collection
    /// </summary>
    [ContentSerializerIgnore]
    public int Count
    {
        get
        {
            return _collection.Count;
        }
    }

    #endregion;

    /// <summary>
    /// Constructor
    /// </summary>
    public KeyedCollection()
    {
        _collection = new List<T>();
        _lookup = new Dictionary<int, int>();
    }

    /// <summary>
    /// Find the neighbor at the given keyed index.
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public T Key(int index)
    {
        return _collection[_lookup[index]];
    }

    #region IEnumerable

    /// <summary>
    /// Enumerator
    /// </summary>
    /// <returns></returns>
    public IEnumerator<T> GetEnumerator()
    {
        foreach (T element in _collection)
        {
            yield return element;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion;
}

Pipeline compiler directives already included too! It's a bit sparse for now, but a start.

3 comments:

  1. Nice post, do you have the details of why the mighty IntermediateSerializer won't deserialize a KeyedCollection?

    Thanks

    ReplyDelete
  2. Just so you know, I just serialized and deserialized a KeyedCollection without any issues. What version of .NET/XNA were you using? it worked for me with 4.0

    ReplyDelete
  3. This was way back with 3.1. Maybe in 4 they added serialization for this - I haven't checked. The class I wrote serves as a very simplified wrapper that only exposes a portion of the functionality. Mostly I was after immutability in my run-time classes.

    ReplyDelete