/// <summary>Initializes a new field, or overwrites an existing one. Can optionally be typed strongly.</summary> /// <param name="fieldName">The field to be added</param> /// <param name="values">IEnumerable containing </param> /// <param name="type"></param> /// <exception cref="ParseFormularException">This exception is thrown if the fieldName contains invalid characters.</exception> /// <exception cref="EmptyBinException">This exception is thrown if values is or contains null.</exception> /// <exception cref="InvalidCastException">This exception is thrown if a value is added to a bin that cannot be cast to the bin's type.</exception> /// <exception cref="BinTypeMismatchException">This exception is thrown if a value is added to a bin that cannot be cast to the bin's type.</exception> /// <exception cref="TypeNotSupportedException">This exception is thrown if the new bin's type is not registered in TypeIdentity.</exception> /// <exception cref="ArgumentNullException">This exception is thrown if attempt is made to add null to a the bin.</exception> public void AssignFrom(string fieldName, IEnumerable values, Type type = null) { if (!fieldName.IsValidFieldName()) { throw new ParseFormularException("\"" + fieldName + "\" is not a valid name for a Message's field. Only use alphanumerics, dots, hyphens and underscores. "); } if (type == null) { if (values == null) { throw new ArgumentNullException("Attempted to Assign null to a new Bin."); } var gen = values.GetType().GenericTypeArguments; // in case en is not generic, pick the first one and reflect if (gen == null || gen.Count() != 1) { var tmp = values.Cast <object>(); // throws ArgumentNullException and InvalidCastException. if (tmp.Count() == 0) { throw new EmptyBinException("Cannot add an empty bin without information about its type. Consider adding type information to AssignFrom()"); } var obj = tmp.FirstOrDefault(); if (obj == null) { throw new ArgumentNullException("Cannot assign null to a new Bin."); } type = obj.GetType(); } else { type = values.GetType().GenericTypeArguments[0]; } } type = TypeIdentity.Instance.FindBaseType(type); // break it down. if (type == null) { throw new TypeNotSupportedException("The assignment for the Field [" + fieldName + "] failed, type is not supported: " + this.Topic); } // replace if type does not match if (!Data.ContainsKey(fieldName) || type != Data[fieldName].GetInnerType()) { Data.Remove(fieldName); Data.Add(fieldName, BinFactory.New(type)); } else { Data[fieldName].Clear(); } Data[fieldName].Add(values); // implicit conversion }
/// <summary>Deep Clones the given Message. Fields of type Message will not be deep cloned.</summary> /// <returns>A new Message</returns> public object Clone() { // might be faster when utilizing binary serialisation. Message m = new Message(); m.Topic = Topic; m.TimeStamp = TimeStamp; foreach (string name in Data.Keys) { var list = Data[name]; var type = list.GetInnerType(); var newList = BinFactory.New(type, list.Count); // deep cloning of all fields, but messages: nested messages are merely a reference by design. bool isPrimitiveType = type.IsPrimitive || type.IsValueType || (type == typeof(string)); if (isPrimitiveType || type == typeof(Message)) { for (int i = 0; i < list.Count; i++) { newList.Add(list[i]); // if primitive -> auto copy. if Message -> reference only } } else { if (typeof(ICloneable).IsAssignableFrom(type)) { for (int i = 0; i < list.Count; i++) { var clone = ((ICloneable)list[i]).Clone(); newList.Add(clone); } } else { throw new SystemException(type.FullName + " cannot be cloned nor copied, while cloning the message " + this.Topic); } } m[name] = newList; // add list to new Message } return(m); }
/// <summary>Deep Clones the given Message. Fields of type Message will not be deep cloned.</summary> /// <returns>A new Message</returns> public object Clone() { Message m = new Message(); m.Topic = Topic; m.TimeStamp = TimeStamp; foreach (string name in Data.Keys) { var list = Data[name]; var type = list.GetInnerType(); var newBin = BinFactory.New(type, list.Count); var typeRecord = TypeIdentity.Instance[type]; if (typeRecord == null) { throw new SystemException(type.FullName + " is not a registered type. Cannot add such a field to Message \"" + this.Topic + "\""); } // removing this line will force creation of empty bins, if the field is not cloneable somehow. if (typeRecord.CloneMethod == CloneBehaviour.Null) { continue; // omit the entire Field } for (int i = 0; i < list.Count; i++) { switch (typeRecord.CloneMethod) { case CloneBehaviour.Null: continue; case CloneBehaviour.Assign: newBin.Add(list[i]); break; case CloneBehaviour.Clone: var clone = (list[i] as ICloneable)?.Clone(); if (clone == null) { throw new SystemException(type.FullName + " cannot be cloned with IClone interface. Check Profile. " + this.Topic); } newBin.Add(clone); break; case CloneBehaviour.Custom: var customClone = typeRecord.CustomClone(list[i]); if (customClone == null) { throw new SystemException(type.FullName + " cannot be cloned with Custom Clone delegate. Check Profile. " + this.Topic); } newBin.Add(customClone); break; default: throw new NotImplementedException("CloneBehaviour " + typeRecord.CloneMethod.ToString() + " unhandled."); } } m[name] = newBin; // add list to new Message } return(m); }
public object Clone() { return(BinFactory.New(this as IEnumerable <T>)); }