public TypeInfo(Type type) { Type = type; StorableTypeAttribute = StorableTypeAttribute.GetStorableTypeAttribute(type); StorableTypeAttributeGuid = string.Empty; Fields = Enumerable.Empty <ComponentInfo <FieldInfo> >(); WriteableProperties = Enumerable.Empty <ComponentInfo <PropertyInfo> >(); ReadableProperties = Enumerable.Empty <ComponentInfo <PropertyInfo> >(); BeforeSerializationHooks = Enumerable.Empty <MethodInfo>(); AfterDeserializationHooks = Enumerable.Empty <MethodInfo>(); Used = 0; Reflect(); }
public void UpdateRegisteredTypes() { foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { if (asm.IsDynamic) { continue; } if (cachedAssemblies.Contains(asm.FullName)) { continue; } cachedAssemblies.Add(asm.FullName); foreach (var t in asm.GetTypes()) { if (typeof(ITransformer).IsAssignableFrom(t) && !t.IsAbstract) { var transformer = (ITransformer)Activator.CreateInstance(t); RegisterTransformer(transformer); } } foreach (var t in asm.GetTypes()) { if (StorableTypeAttribute.IsStorableType(t)) { type2Guid.Add(t, StorableTypeAttribute.GetStorableTypeAttribute(t).Guid); foreach (var guid in StorableTypeAttribute.GetStorableTypeAttribute(t).Guids) { if (guid2Type.ContainsKey(guid)) { throw new PersistenceException($"The GUID {guid} is already used by type {guid2Type[guid]}.", t); } guid2Type.Add(guid, t); } } else if (typeof(IStorableTypeMap).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract) { var knownTypeMap = (IStorableTypeMap)Activator.CreateInstance(t); foreach (var tup in knownTypeMap.KnownStorableTypes) { RegisterType(tup.Item1, tup.Item2); } } } } }
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; } }