Friday, October 13, 2006


Update: The bug has been fixed! Now that's what I call service.




Update: the bug has been reported.



I've been working with a number of classes that expose events. These classes are marked as serializable so that they can be saved to the disk. However, I don't want the events to be serialized, as the built-in serializer will try to serialize all the subscribers to these events, and not all subscribers will be serializable (windows forms, for example). So, following the guidelines I've tagged the events of these classes with the [field:NonSerialized] attribute to prevent the subscribers to these events from being serialized. Unfortunately, this attribute appears to have no effect if the class is written in C++/CLI, although it does work for classes written in C#.


To demonstrate this, consider the following class that implements a String "Text" property and exposes a simple "TextChanged" event that can be used to notify interested parties of changes to it:

[Serializable]
public class TestClass
{
String text_;
public TestClass(String text)
{
text_ = text;
}

public String Text
{
get
{
return text_;
}
set
{
if (text_ != value)
{
text_ = value;
TextChanged(this, new EventArgs());
}
}
}

[field:NonSerialized]
public event EventHandler TextChanged;
}

 


If I wire up the "TextChanged" event to a control on a windows form, I can serialize instances of this class without worrying about it also trying to serialize the windows form as it does so.


Now implement the same class in C++/CLI:

[Serializable]
public ref class TestClass2
{
public:
TestClass2(String^ text)
: text_(text)
{}

property String^ Text
{
String^ get()
{
return text_;
}
void set(String^ text)
{
if (text_ != text)
{
text_ = text;
TextChanged(this, gcnew EventArgs());
}
}
}

[field:NonSerialized]
event EventHandler^ TextChanged;

private:
String^ text_;
};

 


Wiring up the "TextChanged" event of this class up to a windows form control and then try to serialize it causes a "Type 'Form1' in Assembly 'TestApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable" error. This occurs because the serializer has tried to serialize the event (and it's subscriber, the windows form) even though it was explicitly marked with the [field:NonSerialized] attribute.


I think this is just an error in the implementation of [field:NonSerialized] for C++ / CLI: I've tried using Lutz Roeder's Reflector to look at the contents of the assemblies, and it does seem to show that the [field:NonSerialized] attribute is missing from the C++ version.


A work around is to implement the event explicitly in the C++ version of the class by implementing "add" and "remove", and providing a suitable [NonSerialized] delegate as the event's backing store. However, it would obviously be better if the [field:NonSerialized] attribute worked correctly!