internal static XIdentifierBox Box <TType, TId>(XIdentifier <TType, TId> identifier) where TType : class => new XIdentifierBox(identifier) { Type = typeof(TType), IdType = typeof(TId), getId = (obj) => identifier.GetId((TType)obj), canId = (type) => identifier.CanId(type), defaultValue = ReflectionTools.GetDefaultValue(typeof(TId)), equals = (x, y) => identifier.Equals((TType)x, (TType)y), hashCode = (obj) => identifier.GetHashCode((TType)obj), keyEquals = (x, y) => identifier.KeyComparer.Equals((TId)x, (TId)y), keyHash = (obj) => identifier.KeyComparer.GetHashCode((TId)obj) };
protected override bool OnRead <T>(IXReadOperation reader, XType <T> xType, XElement element, Func <object, bool> assign, XObjectArgs args) { Type type = typeof(T); // A serialized reference has no attributes, no elements, some text, is of a type that can be ID'd, // and not of a type that has a registered XTexter string value = element.Value; if (!element.HasAttributes && !element.HasElements && !string.IsNullOrEmpty(value) && xType.Component <XTexter <T> >() == null && Identifier.CanId(type, out Type idType)) { bool idFound = false; object id = null; reader.Read(element, idType, x => { idFound = true; if (!Identifier.KeyComparer.Equals(x, ReflectionTools.GetDefaultValue(idType))) { id = x; } return(true); }, XObjectArgs.DefaultIgnoreElementName); // Schedule a task to assign the object if it shows up in the dictionary reader.AddTask(this, () => { if (!idFound) { return(false); } if (id == null) { return(true); } if (referenceObjects.TryGetValue(id, out object refObject)) { if (refObject == null || type == refObject.GetType()) { return(assign(refObject)); } else { throw new InvalidOperationException( $"Possible collision: the reference object with ID {id} was of expected type {type.Name}, " + $"but that ID resolved to an object of type {refObject.GetType().Name}."); } } return(false); }); return(true); } return(false); }