protected override object Extract(Box box, Type type, Mapper mapper) { var obj = Activator.CreateInstance(type); for (int i = 0; i < box.Values.Kvps.Keys.Count; i++) { var key = box.Values.Kvps.Keys[i]; var value = box.Values.Kvps.Values[i]; string name = mapper.GetString(key); MemberInfo[] mis = type.GetMember(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (mis.Length != 1) { throw new Exception("ambiguous struct member name " + name); } MemberInfo mi = mis[0]; if (StorableAttribute.IsStorable(mi)) { throw new PersistenceException("Don't use storable attributes for structs as all fields are serialized automatically."); } if (mi.MemberType == MemberTypes.Field) { ((FieldInfo)mi).SetValue(obj, mapper.GetObject(value)); } else { throw new Exception("invalid struct member type " + mi.MemberType.ToString()); } } return(obj); }
public ComponentInfo(string name, string fullName, T memberInfo, Type declaringType, StorableAttribute storableAttribute, bool readable, bool writeable) { Name = name; FullName = fullName; MemberInfo = memberInfo; DeclaringType = declaringType; StorableAttribute = storableAttribute; Readable = readable; Writeable = writeable; }
private void Reflect() { var type = Type; if (StorableTypeAttribute != null) { StorableTypeAttributeGuid = StorableTypeAttribute.Guid.ToString().ToUpperInvariant(); string guidPrefix = StorableTypeAttributeGuid; // check constructors if (!type.IsValueType && !type.IsEnum && !type.IsInterface && GetStorableConstructor() == null && GetDefaultConstructor() == null) { throw new PersistenceException("No storable constructor or parameterless constructor found.", type); } var fields = new List <ComponentInfo <FieldInfo> >(); var properties = new List <ComponentInfo <PropertyInfo> >(); var beforeSerializationHooks = new List <MethodInfo>(); var afterDeserializationHooks = new List <MethodInfo>(); if (StorableTypeAttribute.MemberSelection != StorableMemberSelection.AllProperties) { // TODO: improved performance for static fields var fieldInfos = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic) .Where(x => !x.Name.StartsWith("<") && !x.Name.EndsWith("k__BackingField")); // exclude backing fields if (StorableTypeAttribute.MemberSelection == StorableMemberSelection.MarkedOnly) { fieldInfos = fieldInfos.Where(StorableAttribute.IsStorable).ToArray(); } foreach (var field in fieldInfos) { var name = field.Name; var attrib = StorableAttribute.GetStorableAttribute(field); if (attrib != null) { if (!string.IsNullOrEmpty(attrib.Name) && !string.IsNullOrEmpty(attrib.OldName)) { throw new PersistenceException($"Field {field.Name} cannot use Name and OldName at the same time.", type); } if (!string.IsNullOrEmpty(attrib.Name)) { name = attrib.Name; } else if (!string.IsNullOrEmpty(attrib.OldName)) { name = attrib.OldName; } } var nameParts = name.Split('.').ToArray(); var sourceType = type; var tmpGuid = Guid.Empty; for (int i = 0; i < nameParts.Length; i++) { var part = nameParts[i]; if (part == "base") { sourceType = sourceType.BaseType; } else if (Guid.TryParse(part, out tmpGuid)) { if (i != 0 || nameParts.Length != 2) { throw new PersistenceException($"Field {field.Name} has an invalid path.", type); } guidPrefix = tmpGuid.ToString().ToUpper(); break; } else if (i != nameParts.Length - 1) { throw new PersistenceException($"Field {field.Name} has an invalid path.", type); } else { break; } } if (sourceType != type) { name = nameParts[nameParts.Length - 1]; guidPrefix = StorableTypeAttribute.GetStorableTypeAttribute(sourceType).Guid.ToString().ToUpperInvariant(); } else if (tmpGuid != Guid.Empty) { name = nameParts[nameParts.Length - 1]; } fields.Add(new ComponentInfo <FieldInfo>(name, guidPrefix + "." + name, field, type, attrib, true, true)); } } if (StorableTypeAttribute.MemberSelection != StorableMemberSelection.AllFields) { // TODO: improved performance for static properties var propertyInfos = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic) .Where(x => !x.GetIndexParameters().Any()); // exclude indexed properties if (StorableTypeAttribute.MemberSelection == StorableMemberSelection.MarkedOnly) { propertyInfos = propertyInfos.Where(StorableAttribute.IsStorable).ToArray(); } foreach (var property in propertyInfos) { var name = property.Name; var attrib = StorableAttribute.GetStorableAttribute(property); if (attrib != null) { if (!string.IsNullOrEmpty(attrib.Name) && !string.IsNullOrEmpty(attrib.OldName)) { throw new PersistenceException($"Property {property.Name} cannot use Name and OldName at the same time.", type); } if (attrib.AllowOneWay && !string.IsNullOrEmpty(attrib.OldName)) { throw new PersistenceException($"Property {property.Name} cannot use AllowOneWay and OldName at the same time.", type); } if (!string.IsNullOrEmpty(attrib.Name)) { name = attrib.Name; } else if (!string.IsNullOrEmpty(attrib.OldName)) { name = attrib.OldName; } } if ((!property.CanRead || !property.CanWrite) && (attrib == null || !attrib.AllowOneWay && string.IsNullOrEmpty(attrib.OldName))) { throw new PersistenceException($"Property {property.Name} must be readable and writable or have one way serialization explicitly enabled or use OldName.", type); } var nameParts = name.Split('.').ToArray(); var sourceType = type; var tmpGuid = Guid.Empty; for (int i = 0; i < nameParts.Length; i++) { var part = nameParts[i]; if (part == "base") { sourceType = sourceType.BaseType; } else if (Guid.TryParse(part, out tmpGuid)) { if (i != 0 || nameParts.Length != 2) { throw new PersistenceException($"Property {property.Name} has an invalid path.", type); } guidPrefix = tmpGuid.ToString().ToUpper(); break; } else if (i != nameParts.Length - 1) { throw new PersistenceException($"Property {property.Name} has an invalid path.", type); } else { break; } } if (sourceType != type) { name = nameParts[nameParts.Length - 1]; guidPrefix = StorableTypeAttribute.GetStorableTypeAttribute(sourceType).Guid.ToString().ToUpper(); } else if (tmpGuid != Guid.Empty) { name = nameParts[nameParts.Length - 1]; } var declaringType = GetPropertyDeclaringBaseType(property); properties.Add(new ComponentInfo <PropertyInfo>(name, guidPrefix + "." + name, property, declaringType, attrib, property.CanRead, property.CanWrite)); } } var methodInfos = type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic) .Where(StorableHookAttribute.IsStorableHook) .Where(x => x.ReturnType == typeof(void) && !x.GetParameters().Any()); foreach (var method in methodInfos) { foreach (var attrib in StorableHookAttribute.GetStorableHookAttributes(method)) { if (attrib.HookType == HookType.BeforeSerialization) { beforeSerializationHooks.Add(method); } if (attrib.HookType == HookType.AfterDeserialization) { afterDeserializationHooks.Add(method); } } } Fields = fields; WriteableProperties = properties.Where(p => p.Writeable).ToArray(); ReadableProperties = properties.Where(p => p.Readable).ToArray(); BeforeSerializationHooks = beforeSerializationHooks; AfterDeserializationHooks = afterDeserializationHooks; } }