Serialization and Event Publishers

Hi

I have an application consisting of Form A and Class B. Class B stores a set of values that I want to save to file, and is therefore Serializable. However, I also added an event to it so that when one of the values changed, a subscriber (in this case Form A) was notified, and could refresh itself.

What actually happened was that when I attempted to write Class B to a filestream, it failed with a 'NonSerializable object' error - the object in question was the form.

Is this because I've added the form to Class B as a subscriber, and therefore .NET assumes it must also serialize the form? Assuming it is, what is the 'best practice' for dealing with this situation, bearing in mind that I can't mark an event as NonSerializable? Encapsulate Class B within another class that publishes the event? (I was hoping to avoid this, as it takes the responsibility of its own objects away from Class B, and means every change to variables within Class B will have to go through this wrapper).

Many thanks
Gerry
LVL 4
gamesmeisterAsked:
Who is Participating?
 
AlexFMConnect With a Mentor Commented:
I reproduced this situation and found that it happened when event is subscribed. After commenting of line
mc.ArrayListChanged +=new EventHandler(mc_ArrayListChanged);
serialization succeeds. I don't know why this happens, but there is a way to solve this: unsubscribe from event before serialization and subscribe again after serialization. This is my small test, see button1_Click function.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace Test
{
    public class Form1 : System.Windows.Forms.Form
    {
        private System.Windows.Forms.Button button1;
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.Container components = null;

        MyClass mc;
        EventHandler eh;

        public Form1()
        {
            InitializeComponent();

            mc = new MyClass();
            eh = new EventHandler(mc_ArrayListChanged);
            mc.ArrayListChanged += eh;
        }

        private void mc_ArrayListChanged(object sender, EventArgs e)
        {
        }

        private void button1_Click(object sender, System.EventArgs e)
        {
            mc.ArrayListChanged -= eh;    //  unsubscribe

            FileStream stream = new FileStream("C:\\test.bin", FileMode.Create,FileAccess.Write);
            StoreStream(mc, stream);

            mc.ArrayListChanged += eh;    // subscribe again
        }

        private void StoreStream(object data, FileStream stream)
        {
            IFormatter f = new BinaryFormatter();
            f.Serialize(stream, data);
            stream.Close();
        }


        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Windows Form Designer generated code
        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            //
            // button1
            //
            this.button1.Location = new System.Drawing.Point(80, 104);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(120, 32);
            this.button1.TabIndex = 0;
            this.button1.Text = "button1";
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // Form1
            //
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(292, 266);
            this.Controls.Add(this.button1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);

        }
        #endregion

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.Run(new Form1());
        }
    }
}

0
 
AlexFMCommented:
Please show your code.
0
 
gamesmeisterAuthor Commented:
Code as follows (names changed to protect the innocent). When I try to serialize an instance of MyClass, I get the following error
********************************************************************
An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll

Additional information: The type MyForm in Assembly MyExe, Version=1.0.1886.24124, Culture=neutral, PublicKeyToken=null is not marked as serializable.
**********************************************************************

public class MyForm
{
      public MyForm()
      {
            MyClass mc = new MyClass();
            mc.ArrayListChanged +=new EventHandler(mc_ArrayListChanged);
      }
      

      private void mc_ArrayListChanged(object sender, EventArgs e)
      {
            //update list view on Form
      }
}


[Serializable]
public class MyProject
{
      private ArrayList myArray;
      protected event EventHandler arrayListChanged;

      public MyProject()
      {
            myArray = new ArrayList();
      }

      public event EventHandler ArrayListChanged
      {
            add      {arrayListChanged += value;}
            remove {arrayListChanged -= value;}
      }

      public void AddString(string myString)
      {
            myArray.Add(myString);
            if (arrayListChanged != null)
            {
                  OnArrayListChanged(new EventArgs());
            }
      }

      protected void OnArrayListChanged(System.EventArgs e)
      {
            arrayListChanged (this, e);
      }
}
0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
gamesmeisterAuthor Commented:
Sorry, the class should be called MyClass, not MyProject
0
 
AlexFMCommented:
Where is serialization code? It looks like you are trying to serialize MyForm and not MyClass.
0
 
gamesmeisterAuthor Commented:
The Serialization code is in a class library, called from MyForm. It receives an object and a path, and saves it as per the following. The object I'm passing as 'data' is the instance of MyClass (mc). One other thing - this all worked fine until I added the event handler to MyClass

public class BinaryPersistence : IPersistence
{
      private object storedData;
      private Stream stream;
      
      public BinaryPersistence()
      {
      }

      public void StoreFile(object data, string path)
      {
            if (data.Equals(null))
            {
                  throw (new ArgumentNullException("data", "No data passed to store"));
            }
            else
            {
                  if (path.Equals(null))
                  {
                        throw (new ArgumentNullException("path", "Filename cannot be null"));
                  }
            }
      
            stream = new FileStream(path,FileMode.Create,FileAccess.Write);
            StoreStream(data);
      }
      
      private void StoreStream(object data)
      {
            IFormatter f = new BinaryFormatter();
            f.Serialize(stream, data);
            stream.Close();
      }

}
0
 
gamesmeisterAuthor Commented:
From the lack of response, can I assume that it's normally perfectly ok to add Event Handlers to a serializable class, and that there's something else wrong here?
0
 
AlexFMCommented:
Currently I have no answer, I will try to make small test later.
0
 
gamesmeisterAuthor Commented:
That's an excellent workaround, thanks Alex
0
All Courses

From novice to tech pro — start learning today.