More .NET Compact Framework Woes

February 4, 2007 - 4 minute read -
compact framework csharp

I posted previously on a Bug in the .NET Compact Framework with the XmlEnum Attribute with whitespace in the name. Well I've run into some other interesting "features".

The first thing to realize is that the things that work on the Full framework don't work on the Compact Framework.

What Works With Serialization

First thing is the good news: Arrays work everywhere. They work on the Compact Framework and the Full Framework. The downside of course is that Arrays are very inconvenient. You have to manually resize them yourself for example.

The second thing that works are regular ILists. When you map them a collection will be used. Of course one of the big improvements in .NET 2.0 was the introduction of Generics. Generics allow you to have strongly-typed collections without manually implementing them for each specific type.

More Generics Problems

Xml Serialization works if you use IList. But there are problems with Generics interfaces. I guess there are more than problems. Short story is that you can not use IList for XML serialization, it plain just doesn't work.

Here's where the difference between the Full Framework and the Compact Framework come into play. On the Full Framework, you can map to a concrete collection wether it's generic or not. So List will work. Unfortunately this does not work in the Compact Framework.

[XmlElement(Name="foo")]
public List<string> Foos
{
     get { return this.foos }
     set { this.foos = value; }
}

You end up with an exception:

Two mappings for string[].

Stack Trace: at System.Xml.Serialization.TypeContainer.AddType() at System.Xml.Serialization.TypeContainer.AddType() at System.Xml.Serialization.XmlSerializationReflector.AddIXmlSerializableType() at System.Xml.Serialization.XmlSerializationReflector.AddType() at System.Xml.Serialization.XmlSerializationReflector.FindType() at System.Xml.Serialization.XmlSerializationReflector.FindType() .....

So What Do You Do About It

You basically have 2 options:

  1. Use Arrays
  2. Create your own custom, strongly-typed collections classes

Arrays

The advantage is that this is very simple and requires no extra code. The downside as I said above is that you have to write code to manually do array resizing.

public T[] AppendItem<T>(T[] theArray, T newItem) {
    T[] newArray = new T[theArray.Length + 1];
    Array.Copy(theArray, newArray, theArray.Length);
    newArray[newArray.Length - 1] = newItem;
    return newArray;
}

Custom, Strongly-Typed Collections

If you don't want to use Arrays and deal with manually resizing them, you can build your own Collections classes for each of your types.

Create your strongly-typed collection:

[Serializable]
[EditorBrowsable(EditorBrowsableState.Advanced)]
public class EmployeeCollection : ArrayList {
    public Employee Add(Employee obj) {
        base.Add(obj);
       return obj;
    }</p>
<p>   public Employee Add() {
       return Add(new Employee());
   }</p>
<p>   public void Insert(int index, Employee obj) {
       base.Insert(index, obj);
   }</p>
<p>   public void Remove(Employee obj) {
       base.Remove(obj);
   }</p>
<p>   new public Employee this[int index] {
       get { return (Employee) base[index]; }
       set { base[index] = value; }
   }
}

Then use that collection in your class to map:

[XmlRoot("company")]
class Company {
    private EmployeeCollection employees;</p>
<p>    [XmlElement(Type=typeof(Employee),ElementName="employee",IsNullable=false)]
    [EditorBrowsable(EditorBrowsableState.Advanced)]
    public EmployeeCollection Employees {
        get { return this.employees; }
        set { this.employees = value; }
    }
}

Pick Your Poison

The problems with Generics collections in the .NET Compact Framework seem like yet another bug. So, pick your poison and choose a workaround. Whichever seems simpler to you. Hope that helps someone out.