/// <summary> /// Adds an item to the list /// /// * If the list has no type the type will be infered from the given object /// * If the list has no type and `null` is added the type will be set as `object` /// </summary> /// <param name="obj"></param> /// <exception cref="Exception">If the given object is not assignable to the list type</exception> public void Add(object obj) { if (null == m_Items) { // Special case when adding an element and we have no items // Dynamically create the list and properties to be strongly typed. We must use activator in this situation var type = obj?.GetType() ?? typeof(object); m_Items = Activator.CreateInstance(typeof(List <>).MakeGenericType(type)); m_ItemsProperty = CreateItemsProperty(type); m_PropertyBag.AddProperty(m_ItemsProperty); } if (obj is UTinyObject) { // Special case for tiny object. We DON'T want to retain the given instance. // Instead we create a new object and deep copy the values in. This way the object // Will propegate version changes to this list var v = new UTinyObject(m_Registry, m_Type, this); v.CopyFrom((UTinyObject)obj); var typedList = (IListProperty <UTinyList, UTinyObject>)m_ItemsProperty; typedList.Add(this, v); } else { try { var converted = Convert(obj, m_ItemsProperty.ItemType); m_ItemsProperty.AddObject(this, converted); } catch (Exception e) { throw new Exception($"UTinyList.Add Type mismatch expected instance of Type=[{m_ItemsProperty.ItemType}] received Type=[{obj?.GetType()}]", e); } } }
/// <summary> /// This method does a lot of magic /// You can associate ANY value with a name and the correct property will be generated for you /// </summary> public object this[string key] { get { // Incurs a dictionary lookup var property = m_PropertyBag.FindProperty(key); // Must asset to avoid undefined behaviour Assert.IsNotNull(property, $"Property '{key}' does not exist on object"); var container = (IPropertyContainer)this; // This works well since the implementation details are abstracted // i.e. We don't care how the value is unpacked (List, Dictionary, NativeArray etc) return(property.GetObjectValue(container)); } set { // Incurs a dictionary lookup var property = m_PropertyBag.FindProperty(key); if (null == property) { // Auto-generate a dynamic property for the user var type = value?.GetType() ?? typeof(object); property = CreateDynamicProperty(key, type); Assert.IsNotNull(property); m_PropertyBag.AddProperty(property); if (null == m_DynamicValues) { m_DynamicValues = new Dictionary <string, object>(); } // Setup the underlying storage // This breaks our abstraction but must be done at some point if (typeof(UTinyObject) == type) { var obj = new UTinyObject(m_Object.Registry, UTinyType.Reference.None); obj.CopyFrom(value as UTinyObject); m_DynamicValues.Add(key, obj); } else if (typeof(UTinyList) == type) { var obj = new UTinyList(m_Object.Registry, UTinyType.Reference.None); obj.CopyFrom(value as UTinyList); m_DynamicValues.Add(key, obj); } else { m_DynamicValues.Add(key, value); } } else { // @TODO There is an unhandled case here when we encounter a type mis-match, we need to detect this and throw try { property.SetObjectValue(this, value); } catch (InvalidCastException) { Debug.LogError($"Could not cast {value.GetType()} to {property.ValueType}. Value is '{value}'."); throw; } } } }