/// <summary>
        /// Deletes a property from the schema.
        /// </summary>
        /// <param name="name"> The name of the property to delete. </param>
        /// <returns> A new schema without the property. </returns>
        public HiddenClassSchema DeleteProperty(string name)
        {
            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;

            if (this.m_deleteTransitions != null)
            {
                this.m_deleteTransitions.TryGetValue(name, out newSchema);
            }

            if (newSchema == null)
            {
                // Create a new schema based on this one.
                var properties = this.m_properties == null?CreatePropertiesDictionary() : new Dictionary <string, SchemaProperty>(this.m_properties);

                if (properties.Remove(name) == false)
                {
                    throw new InvalidOperationException(string.Format("The property '{0}' does not exist.", name));
                }
                newSchema = new HiddenClassSchema(properties, this.NextValueIndex);

                // Add a transition to the new schema.
                if (this.m_deleteTransitions == null)
                {
                    this.m_deleteTransitions = new Dictionary <string, HiddenClassSchema>(1);
                }
                this.m_deleteTransitions.Add(name, newSchema);
            }

            return(newSchema);
        }
 /// <summary>
 /// Enumerates the property names and values for this schema.
 /// </summary>
 /// <param name="values"> The array containing the property values. </param>
 /// <returns> An enumerable collection of property names and values. </returns>
 public IEnumerable <PropertyNameAndValue> EnumeratePropertyNamesAndValues(object[] values)
 {
     if (this.m_properties == null)
     {
         this.m_properties = CreatePropertiesDictionary();
     }
     this.m_parent = null; // Prevents the properties dictionary from being stolen while an enumeration is in progress.
     return(this.m_properties.Select(pair => new PropertyNameAndValue(pair.Key, new PropertyDescriptor(values[pair.Value.Index], pair.Value.Attributes))));
 }
        /// <summary>
        /// Adds a property to the schema.
        /// </summary>
        /// <param name="name"> The name of the property to add. </param>
        /// <param name="attributes"> The property attributes. </param>
        /// <returns> A new schema with the extra property. </returns>
        public HiddenClassSchema AddProperty(string name, PropertyAttributes attributes)
        {
            // Package the name and attributes into a struct.
            var transitionInfo = new TransitionInfo {
                Name = name, Attributes = attributes
            };

            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;

            if (this.m_addTransitions != null)
            {
                this.m_addTransitions.TryGetValue(transitionInfo, out newSchema);
            }

            if (newSchema == null)
            {
                if (this.m_parent == null)
                {
                    // Create a new schema based on this one.  A complete copy must be made of the properties hashtable.
                    var properties = new Dictionary <string, SchemaProperty>(this.m_properties)
                    {
                        { name, new SchemaProperty(this.NextValueIndex, attributes) }
                    };
                    newSchema = new HiddenClassSchema(properties, this.NextValueIndex + 1, this, transitionInfo);
                }
                else
                {
                    // Create a new schema based on this one.  The properties hashtable is "given
                    // away" so a copy does not have to be made.
                    if (this.m_properties == null)
                    {
                        this.m_properties = CreatePropertiesDictionary();
                    }
                    this.m_properties.Add(name, new SchemaProperty(this.NextValueIndex, attributes));
                    newSchema         = new HiddenClassSchema(this.m_properties, this.NextValueIndex + 1, this, transitionInfo);
                    this.m_properties = null;
                }


                // Add a transition to the new schema.
                if (this.m_addTransitions == null)
                {
                    this.m_addTransitions = new Dictionary <TransitionInfo, HiddenClassSchema>(1);
                }
                this.m_addTransitions.Add(transitionInfo, newSchema);
            }

            return(newSchema);
        }
        /// <summary>
        /// Modifies the attributes for a property in the schema.
        /// </summary>
        /// <param name="name"> The name of the property to modify. </param>
        /// <param name="attributes"> The new attributes. </param>
        /// <returns> A new schema with the modified property. </returns>
        public HiddenClassSchema SetPropertyAttributes(string name, PropertyAttributes attributes)
        {
            // Package the name and attributes into a struct.
            var transitionInfo = new TransitionInfo {
                Name = name, Attributes = attributes
            };

            // Check if there is a transition to the schema already.
            HiddenClassSchema newSchema = null;

            if (this.m_modifyTransitions != null)
            {
                this.m_modifyTransitions.TryGetValue(transitionInfo, out newSchema);
            }

            if (newSchema == null)
            {
                // Create the properties dictionary if it hasn't already been created.
                if (this.m_properties == null)
                {
                    this.m_properties = CreatePropertiesDictionary();
                }

                // Check the attributes differ from the existing attributes.
                SchemaProperty propertyInfo;
                if (this.m_properties.TryGetValue(name, out propertyInfo) == false)
                {
                    throw new InvalidOperationException(string.Format("The property '{0}' does not exist.", name));
                }
                if (attributes == propertyInfo.Attributes)
                {
                    return(this);
                }

                // Create a new schema based on this one.
                var properties = new Dictionary <string, SchemaProperty>(this.m_properties);
                properties[name] = new SchemaProperty(propertyInfo.Index, attributes);
                newSchema        = new HiddenClassSchema(properties, this.NextValueIndex);

                // Add a transition to the new schema.
                if (this.m_modifyTransitions == null)
                {
                    this.m_modifyTransitions = new Dictionary <TransitionInfo, HiddenClassSchema>(1);
                }
                this.m_modifyTransitions.Add(transitionInfo, newSchema);
            }

            return(newSchema);
        }
 /// <summary>
 /// Creates a new HiddenClassSchema instance from an add operation.
 /// </summary>
 private HiddenClassSchema(Dictionary <string, SchemaProperty> properties, int nextValueIndex, HiddenClassSchema parent, TransitionInfo addPropertyTransitionInfo)
     : this(properties, nextValueIndex)
 {
     this.m_parent = parent;
     this.m_addPropertyTransitionInfo = addPropertyTransitionInfo;
 }