Tuesday, May 21, 2013

XmlSerialization - why two runtime behaviour for class with read-only properties?

Following on from the previous blog post, and just to recap, there are at least two ways to write a class with read only property.

The old way is:

    [ Serializable ]
    public class PersonReadOnly {
        String m_Name;
        Int32 m_Age;
        public String Name {
            get{ return m_Name; }
        }
        public Int32 Age { 
            get{ return m_Age; }
        }

        public PersonReadOnly() {
        }

        public PersonReadOnly (String name, Int32 age) {
            this.m_Name = name;
            this.m_Age = age;
        }
    }

and the other way is to use private setter like this:

    [ Serializable ]
    public class PersonReadOnly1 {
        String m_Name;
        Int32 m_Age;

        public String Name {
            get{ return m_Name; }
            private set { m_Name = value; }
        }
        public Int32 Age { 
            get{ return m_Age; }
            private set { m_Age = value; }
        }

        public PersonReadOnly1() {
        }

        public PersonReadOnly1 (String name, Int32 age) {
            this.Name = name;
            this.Age = age;
        }
    }


These two classes functionally are the same. However, the XmlSerializer exhibits very different behaviour depending on how you write the class. This to me seem rather odd.

In the case of PersonReadOnly, there is no runtime exception, except that the state of the class, namely Name and Age, are not serialized as in agreement with the specification. So when the stream is deserialized, the values of Name and Age are that of the null and 0, respectively. Fair enough!

But when one writes the class as in the style promoted in PersonReadOnly1, the runtime behaviour is radically different. XmlSerializer throws the following InvalidOperationException, which is caused by the exception in sgen.exe,
System.InvalidOperationException: Unable to generate a temporary class (result=1).
error CS0200: Property or indexer 'TestSerialization.PersonReadOnly1.Name' cannot be assigned to -- it is read only
error CS0200: Property or indexer 'TestSerialization.PersonReadOnly1.Age' cannot be assigned to -- it is read only

   at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence)
   at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Assembly assembly, Hashtable assemblies)
   at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace)
   at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
   at System.Xml.Serialization.XmlSerializer..ctor(Type type)
   at TestSerialization.TestXmlSerialization.SerializeClassWithNonPublicSetter_PersonReadOnly1() in E:\Projects\SerializationDemo2\TestSerialization\TestXmlSerialization.cs:line 74


The question is: should CLR exhibit the same runtime behaviour regardless of how the class is constructed?

No comments:

Post a Comment