views:

509

answers:

5

Has anyone else run into this problem before? I've got a method that calls a generic method with a delegate, inside of a generic class. I've marked the class as Serializable, and it serializes without complaint. But, when I try to deserialize an object of this class, it pegs the CPU and hangs the machine.

Code example:

public delegate T CombinationFunctionDelegate<T,U,V>(U a, V b);

    [Serializable]
public class SDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public SDictionary()
        : base()
    {
    }

    protected SDictionary(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {}

    [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
    }

    public List<ListItem> ToListItems()
    {
        return Convert(delegate(TKey key, TValue value)
        {
            return new ListItem(key.ToString(), value.ToString());
        });
    }

    public List<U> Convert<U>(CombinationFunctionDelegate<U, TKey, TValue> converterFunction)
    {
        List<U> res = new List<U>();
        foreach (TKey key in Keys)
            res.Add(converterFunction(key, this[key]));

        return res;
    }
}

I can put an instance of this class into ViewState (for example) just fine, but when I try to extract the object from ViewState again, the CPU on the machine spikes and the deserialization call never returns (ie, infinite loop).

When I remove the ToListItems() method, everything works wonderfully. Is this really weird, or do I just not understand serialization? =)

A: 

First of all, Dictionary<> already implements ISerializable, so you don't need to specify that explicity!

Second, you override GetObjectData(), but you don't seem to call Dictionary.GetObjectData(), so the dictionary may not be getting deserialised? Hence, when you access this.Keys, you end up with "an issue".

Yes, I'm thinking out loud here ;)

Perhaps you could try this:

public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
  // deserialize the dictionary first
  base.GetObjectData(info, context);

  // the rest of your code
  // ...
}

I haven't tried or compiled this, but it could be something to consider?

Good luck :)

OJ
Hey thanks for the thoughts. I edited my post to show a simpler version of that class that gives the same problem, with your suggestions included. I still think it's really weird =)
Badjer
+1  A: 

This is the code I currently have, which works fine?

    [Serializable]
    public class SDictionary<TKey, TValue> : Dictionary<TKey, TValue>
    {
        public SDictionary()
            : base()
        {
        }

        protected SDictionary(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }

        public List<ListItem> ToListItems()
        {
            return this.Convert(delegate(TKey key, TValue value)
            {
                return new ListItem(key.ToString(), value.ToString());
            });
        }

        public List<U> Convert<U>(CombinationFunctionDelegate<U, TKey, TValue> converterFunction)
        {
            List<U> res = new List<U>();
            foreach (TKey key in Keys)
                res.Add(converterFunction(key, this[key]));

            return res;
        }


    }

    class Program
    {

        static void Main(string[] args)
        {
            SDictionary<string, string> b = new SDictionary<string, string>();
            b.Add("foo", "bar");

            System.IO.MemoryStream memStream = new System.IO.MemoryStream();
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter f = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            f.Serialize(memStream, b);
            memStream.Position = 0;

            b = f.Deserialize(memStream) as SDictionary<string, string>;
        }

    }

Does that help at all?

Edit: Tweaked again.

OJ
A: 

Could it be that the anonymous method has a reference to the instance that it resides in? I can't answer that.

Your comment indicates that this is possible. Here's the simplest way out: don't use anonymous methods.

public ListItem ToListItem(TKey key, TValue value)
{
  return new ListItem(key.ToString(), value.ToString());
}


What I can answer, is that the methods on this class can be issued against the public contract of Dictionary< T, U >, and so there is no need for this class when you could write extension methods against Dictionary< T, U > (assuming C# 3)

Something like this (freehand code so may not be 100% correct)

public static List<ListItem> ToListItems(this Dictionary<T, U> source)
{
  return source
    .Select(x => new ListItem(x.key.ToString(), x.value.ToString()))
    .ToList();
}

public static List<V> Convert<V>
(
  this Dictionary<T, U> source,
  Func<T, U, V> converterFunction
)
{
  return source
    .Select(x => converterFunction(x.Key, x.Value))
    .ToList();
}
David B
Ahhhh - good thought - the delegate's closure would include the object itself, so it could get into an infinite loop when deserializing the object. I would use C# 3, but it's unfortunately not an option at the moment =(Thanks for the help!
Badjer
A: 

Are you using VS2008 SP1? There's a known problem with SP1.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=361615

A: 

Here's the knowledge base article on the bug, too, if anyone needs it: http://support.microsoft.com/?id=957543

Badjer