/// <summary>
 /// Serialize fields within an object instance
 /// </summary>
 /// <param name="Instance">The object to serializer</param>
 /// <param name="Fields">Array of fields within this object</param>
 /// <param name="Types">Lookup of known types</param>
 /// <param name="Instances">Lookup of known object instances</param>
 /// <param name="Writer">Binary writer for output data</param>
 static void SerializeObjectFields(object Instance, FieldInfo[] Fields, HashList <Type> Types, HashList <object> Instances, BinaryWriter Writer)
 {
     foreach (FieldInfo Field in Fields)
     {
         object Element = Field.GetValue(Instance);
         SerializeElement(Field.FieldType, Element, Types, Instances, Writer);
     }
 }
        /// <summary>
        /// Serialize an object hierarchy to a stream, starting at the given root object
        /// </summary>
        /// <param name="RootObject">The root object to serialize</param>
        /// <param name="FileName">Output stream for the binary data</param>
        public static void Serialize(object RootObject, Stream OutputStream)
        {
            HashList <Type>   Types     = new HashList <Type>();
            HashList <object> Instances = new HashList <object>();

            using (BinaryWriter Writer = new BinaryWriter(OutputStream, Encoding.UTF8, true))
            {
                SerializeInstance(RootObject, Types, Instances, Writer);
            }
        }
 /// <summary>
 /// Serialize a single value or array element
 /// </summary>
 /// <param name="ElementType">Type of the element</param>
 /// <param name="ElementValue">Value to serialize</param>
 /// <param name="Types">List of known types</param>
 /// <param name="Instances">List of known object instances</param>
 /// <param name="Writer">Writer for the output data</param>
 static void SerializeElement(Type ElementType, object ElementValue, HashList <Type> Types, HashList <object> Instances, BinaryWriter Writer)
 {
     if (ElementType == typeof(Boolean))
     {
         Writer.Write((Boolean)ElementValue);
     }
     else if (ElementType == typeof(Int32))
     {
         Writer.Write((Int32)ElementValue);
     }
     else if (ElementType.IsEnum)
     {
         Type UnderlyingType = ElementType.GetEnumUnderlyingType();
         SerializeElement(UnderlyingType, Convert.ChangeType(ElementValue, UnderlyingType), Types, Instances, Writer);
     }
     else if (ElementType.IsClass || ElementType.IsArray || ElementType.IsInterface)
     {
         if (ElementValue == null)
         {
             Writer.Write(-1);
         }
         else
         {
             int Index = Instances.IndexOf(ElementValue);
             if (Index == -1)
             {
                 Writer.Write(Instances.Count);
                 SerializeInstance(ElementValue, Types, Instances, Writer);
             }
             else
             {
                 Writer.Write(Index);
             }
         }
     }
     else
     {
         FieldInfo[] Fields = FindAllFields(ElementType);
         if (Fields.Length > 0)
         {
             SerializeObjectFields(ElementValue, Fields, Types, Instances, Writer);
         }
         else
         {
             throw new NotImplementedException();
         }
     }
 }
        /// <summary>
        /// Serialize an object instance to a BinaryWriter
        /// </summary>
        /// <param name="ThisObject">The object to serialize</param>
        /// <param name="Types">List of types which have been serialized before. If the type of this object is new, it will be added to the list.</param>
        /// <param name="Instances">List of object instances which have already been serialized. The given object must NOT be in this list.</param>
        /// <param name="Writer">The writer for output data</param>
        static void SerializeInstance(object ThisObject, HashList <Type> Types, HashList <object> Instances, BinaryWriter Writer)
        {
            Type ThisType = ThisObject.GetType();

            // Write the type
            int TypeIdx = Types.IndexOf(ThisType);

            if (TypeIdx == -1)
            {
                Types.Add(ThisType);
                Writer.Write((int)(Types.Count - 1));
                Writer.Write(ThisType.FullName);
            }
            else
            {
                Writer.Write(TypeIdx);
            }

            // Now write the instance data
            Instances.Add(ThisObject);
            if (ThisType.IsArray)
            {
                Array ThisArrayObject = (Array)ThisObject;
                for (int DimensionIdx = 0; DimensionIdx < ThisArrayObject.Rank; DimensionIdx++)
                {
                    Writer.Write(ThisArrayObject.GetLength(DimensionIdx));
                }

                Type ElementType = ThisArrayObject.GetType().GetElementType();
                foreach (int[] Indices in EnumerateArrayIndices(ThisArrayObject))
                {
                    SerializeElement(ElementType, ThisArrayObject.GetValue(Indices), Types, Instances, Writer);
                }
            }
            else if (ThisType == typeof(string))
            {
                Writer.Write((string)ThisObject);
            }
            else
            {
                SerializeObjectFields(ThisObject, FindAllFields(ThisType), Types, Instances, Writer);
            }
        }