internal void ApplyDefaultBehaviour() { if (type.BaseType != null && model.FindWithoutAdd(type.BaseType) == null && GetContractFamily(type.BaseType, null) != MetaType.AttributeFamily.None) { model.FindOrAddAuto(type.BaseType, true, false, false); } object[] typeAttribs = type.GetCustomAttributes(true); AttributeFamily family = GetContractFamily(type, typeAttribs); bool isEnum = type.IsEnum; if (family == AttributeFamily.None && !isEnum) { return; // and you'd like me to do what, exactly? } BasicList partialIgnores = null, partialMembers = null; int dataMemberOffset = 0, implicitFirstTag = 1; bool inferTagByName = model.InferTagFromNameDefault; ImplicitFields implicitMode = ImplicitFields.None; for (int i = 0; i < typeAttribs.Length; i++) { Attribute item = (Attribute)typeAttribs[i]; if (!isEnum && item is ProtoIncludeAttribute) { ProtoIncludeAttribute pia = (ProtoIncludeAttribute)item; AddSubType(pia.Tag, pia.KnownType); } if (item is ProtoPartialIgnoreAttribute) { if (partialIgnores == null) { partialIgnores = new BasicList(); } partialIgnores.Add(((ProtoPartialIgnoreAttribute)item).MemberName); } if (!isEnum && item is ProtoPartialMemberAttribute) { if (partialMembers == null) { partialMembers = new BasicList(); } partialMembers.Add(item); } if (!isEnum && item is ProtoContractAttribute) { ProtoContractAttribute pca = (ProtoContractAttribute)item; dataMemberOffset = pca.DataMemberOffset; if (pca.InferTagFromNameHasValue) { inferTagByName = pca.InferTagFromName; } implicitMode = pca.ImplicitFields; UseConstructor = !pca.SkipConstructor; if (pca.ImplicitFirstTag > 0) { implicitFirstTag = pca.ImplicitFirstTag; } } } if (implicitMode != ImplicitFields.None) { family &= AttributeFamily.ProtoBuf; // with implicit fields, **only** proto attributes are important } MethodInfo[] callbacks = null; BasicList members = new BasicList(); foreach (MemberInfo member in type.GetMembers(isEnum ? BindingFlags.Public | BindingFlags.Static : BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { if (member.DeclaringType != type) { continue; } if (member.IsDefined(typeof(ProtoIgnoreAttribute), true)) { continue; } if (partialIgnores != null && partialIgnores.Contains(member.Name)) { continue; } bool forced = false, isPublic, isField; Type effectiveType; switch (member.MemberType) { case MemberTypes.Property: PropertyInfo property = (PropertyInfo)member; effectiveType = property.PropertyType; isPublic = property.GetGetMethod(false) != null; isField = false; goto ProcessMember; case MemberTypes.Field: FieldInfo field = (FieldInfo)member; effectiveType = field.FieldType; isPublic = field.IsPublic; isField = true; ProcessMember: switch (implicitMode) { case ImplicitFields.AllFields: if (isField) { forced = true; } break; case ImplicitFields.AllPublic: if (isPublic) { forced = true; } break; } if (effectiveType.IsSubclassOf(typeof(Delegate))) { continue; // we just don't like delegate types ;p } ProtoMemberAttribute normalizedAttribute = NormalizeProtoMember(member, family, forced, isEnum, partialMembers, dataMemberOffset, inferTagByName); if (normalizedAttribute != null) { members.Add(normalizedAttribute); } break; case MemberTypes.Method: if (isEnum) { continue; } MethodInfo method = (MethodInfo)member; object[] memberAttribs = Attribute.GetCustomAttributes(method); if (memberAttribs != null && memberAttribs.Length > 0) { CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoBeforeSerializationAttribute", ref callbacks, 0); CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoAfterSerializationAttribute", ref callbacks, 1); CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoBeforeDeserializationAttribute", ref callbacks, 2); CheckForCallback(method, memberAttribs, "ProtoBuf.ProtoAfterDeserializationAttribute", ref callbacks, 3); CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnSerializingAttribute", ref callbacks, 4); CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnSerializedAttribute", ref callbacks, 5); CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnDeserializingAttribute", ref callbacks, 6); CheckForCallback(method, memberAttribs, "System.Runtime.Serialization.OnDeserializedAttribute", ref callbacks, 7); } break; } } ProtoMemberAttribute[] arr = new ProtoMemberAttribute[members.Count]; members.CopyTo(arr, 0); if (inferTagByName || implicitMode != ImplicitFields.None) { Array.Sort(arr); int nextTag = implicitFirstTag; foreach (ProtoMemberAttribute normalizedAttribute in arr) { if (!normalizedAttribute.TagIsPinned) // if ProtoMember etc sets a tag, we'll trust it { normalizedAttribute.Rebase(nextTag++); } } } foreach (ProtoMemberAttribute normalizedAttribute in arr) { ValueMember vm = ApplyDefaultBehaviour(isEnum, normalizedAttribute); if (vm != null) { Add(vm); } } if (callbacks != null) { SetCallbacks(Coalesce(callbacks, 0, 4), Coalesce(callbacks, 1, 5), Coalesce(callbacks, 2, 6), Coalesce(callbacks, 3, 7)); } }
/// <summary> /// Apply include mappings via assembly reflection (<see cref="ProtoBaseAttribute"/>).<br /> /// Important: This must be called before any serialization happen. /// </summary> /// <param name="model">The RuntimeTypeModel</param> /// <param name="assemblyRegexFilter">The regex assembly filer</param> /// <param name="implicitFields">The default import of base type fields, if nothing is specified.</param> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentNullException"></exception> /// <exception cref="InvalidDataException"></exception> public static void ApplyProtoBase(this RuntimeTypeModel model, Regex assemblyRegexFilter, ImplicitFields implicitFields = ImplicitFields.AllPublic) { model.ApplyProtoBase(assembly => assemblyRegexFilter == null || assemblyRegexFilter.IsMatch(assembly.FullName), implicitFields); }
/// <summary> /// Apply include mappings via assembly reflection (<see cref="ProtoBaseAttribute"/>).<br /> /// Important: This must be called before any serialization happen. /// </summary> /// <param name="model">The RuntimeTypeModel</param> /// <param name="assemblyFilter">The custom assembly filter</param> /// <param name="implicitFields">The default import of base type fields, if nothing is specified.</param> /// <exception cref="ArgumentException"></exception> /// <exception cref="InvalidDataException"></exception> public static void ApplyProtoBase(this RuntimeTypeModel model, Func <Assembly, bool> assemblyFilter, ImplicitFields implicitFields = ImplicitFields.AllPublic) { AppDomain.CurrentDomain.GetAssemblies().Where(a => assemblyFilter?.Invoke(a) != false) .AsParallel().SelectMany(a => a.GetTypes().Where(t => t.IsClass && t.GetCustomAttributes <ProtoBaseAttribute>(false).Any())) .ForAll(t => { foreach (var attr in t.GetCustomAttributes <ProtoBaseAttribute>(false)) { if (attr.ConcreteType != null) { if (t.IsGenericType) { if (!attr.ConcreteType.IsGenericType || attr.ConcreteType.GetGenericTypeDefinition() != t) { throw new ArgumentException("Concrete type must be assignable from this type"); } } else if (!t.IsAssignableFrom(attr.ConcreteType)) { throw new ArgumentException("Concrete type must be assignable from this type"); } } var type = attr.ConcreteType ?? t; if (type.BaseType == null) { throw new ArgumentException("BaseType cannot be null"); } if (type.IsGenericType && type.GenericTypeArguments.Length == 0) { throw new InvalidDataException($"Cannot add generic type without concrete definition. '{type.FullName}'"); } Type baseType; if (attr.InterfaceType != null) { if (!attr.InterfaceType.IsInterface) { throw new ArgumentException($"The attributed InterfaceType must be an interface. '{attr.InterfaceType.FullName}'"); } if (!attr.InterfaceType.IsAssignableFrom(type)) { throw new ArgumentException( $"BaseType '{attr.InterfaceType.FullName}' must be assignable from type {type.FullName}."); } baseType = attr.InterfaceType; } else { if (type.BaseType == typeof(object)) { baseType = type.GetInterfaces().FirstOrDefault(); if (baseType == null) { throw new ArgumentException($"BaseType cannot be of type '{typeof(object).FullName}' and no interfaces are defined."); } } else { baseType = type.BaseType; } } var metaType = model.Add(type, true); if (metaType.BaseType != null && metaType.BaseType.Type != baseType) { throw new InvalidDataException( $"Type '{type.FullName}' has already an assigned base type '{metaType.BaseType.Type.FullName}'"); } MetaType baseMetaType; if (baseType.IsClass && baseType.GetCustomAttribute <DataContractAttribute>() == null && baseType.GetCustomAttribute <ProtoContractAttribute>() == null && model.GetTypes().OfType <MetaType>().All(a => a.Type != baseType)) { baseMetaType = model.Add(baseType, true); void ApplyImplicitFields(BindingFlags bindingFlags) { var fields = baseType .GetProperties(bindingFlags | BindingFlags.Instance | BindingFlags.DeclaredOnly) .Cast <MemberInfo>() .Concat(baseType.GetFields( bindingFlags | BindingFlags.Instance | BindingFlags.DeclaredOnly)).ToList(); if (fields.Any(a => a.GetCustomAttribute <DataMemberAttribute>() != null || a.GetCustomAttribute <ProtoMemberAttribute>() != null)) { return; } var fieldNumber = 1; foreach (var name in fields.Where(x => x.GetCustomAttribute <IgnoreDataMemberAttribute>() == null && x.GetCustomAttribute <ProtoIgnoreAttribute>() == null && x.GetCustomAttribute <CompilerGeneratedAttribute>() == null) .Select(a => a.Name)) { baseMetaType.Add(fieldNumber++, name); } } switch (implicitFields) { case ImplicitFields.AllPublic: ApplyImplicitFields(BindingFlags.Public); break; case ImplicitFields.AllFields: ApplyImplicitFields(BindingFlags.Public | BindingFlags.NonPublic); break; case ImplicitFields.None: // Do noting break; } } else { baseMetaType = model.Add(baseType, true); } var rootMetaType = baseMetaType; while (rootMetaType.BaseType != null) { rootMetaType = rootMetaType.BaseType; } void ValidateMetaTypes(MetaType mt) { if (mt.HasSubtypes) { if (mt.GetSubtypes().Any(a => a.FieldNumber == attr.FieldNumber)) { throw new InvalidDataException( $"Cannot assign field number '{attr.FieldNumber}' for '{type.FullName}' on '{baseType.FullName}'. It's already being assigned to '{mt.Type.FullName}' in the hierarchy."); } foreach (var en in mt.GetSubtypes()) { ValidateMetaTypes(en.DerivedType); } } } ValidateMetaTypes(rootMetaType); baseMetaType.AddSubType(attr.FieldNumber, type); } }); }
/// <summary> /// Apply include mappings via assembly reflection (<see cref="ProtoBaseAttribute"/>).<br /> /// Important: This must be called before any serialization happen. /// </summary> /// <param name="model">The RuntimeTypeModel</param> /// <param name="implicitFields">The default import of base type fields, if nothing is specified.</param> /// <exception cref="ArgumentException"></exception> /// <exception cref="InvalidDataException"></exception> public static void ApplyProtoBase(this RuntimeTypeModel model, ImplicitFields implicitFields = ImplicitFields.AllPublic) { model.ApplyProtoBase(assembly => true, implicitFields); }