Beispiel #1
0
        /// <summary>
        /// Creates a new CerasSerializer, be sure to check out the tutorial.
        /// </summary>
        public CerasSerializer(SerializerConfig config = null)
        {
            Config = config ?? new SerializerConfig();

            if (Config.ExternalObjectResolver == null)
            {
                Config.ExternalObjectResolver = new ErrorResolver();
            }

            if (Config.Advanced.UseReinterpretFormatter && Config.VersionTolerance.Mode != VersionToleranceMode.Disabled)
            {
                throw new NotSupportedException("You can not use 'UseReinterpretFormatter' together with version tolerance. Either disable version tolerance, or use the old formatter for blittable types by setting 'Config.Advanced.UseReinterpretFormatter' to false.");
            }

            if (Config.Advanced.AotMode != AotMode.None && Config.VersionTolerance.Mode != VersionToleranceMode.Disabled)
            {
                throw new NotSupportedException("You can not use 'AotMode.Enabled' and version tolerance at the same time for now. If you would like this feature implemented, please open an issue on GitHub explaining your use-case, or join the Discord server.");
            }

            TypeBinder          = Config.Advanced.TypeBinder ?? new NaiveTypeBinder();
            DiscardObjectMethod = Config.Advanced.DiscardObjectMethod;

            _userResolvers = Config.OnResolveFormatter.ToArray();

            // Int, Float, Enum, ...
            _resolvers.Add(new PrimitiveResolver(this));

            // Fast native copy for unmanaged types;
            // can not handle generic structs like ValueTuple<> because they always have to be ".IsAutoLayout"
            _resolvers.Add(new ReinterpretFormatterResolver(this));

            // DateTime, Guid, KeyValuePair, Tuple, ...
            _resolvers.Add(new StandardFormatterResolver(this));

            // Array, List, Dictionary, ICollection<T>, ...
            _resolvers.Add(new CollectionFormatterResolver(this));

            // String Formatter should never be wrapped in a RefFormatter, that's too slow and not necessary
            IFormatter stringFormatter;

            if (Config.Advanced.SizeLimits.MaxStringLength < uint.MaxValue)
            {
                stringFormatter = new MaxSizeStringFormatter(Config.Advanced.SizeLimits.MaxStringLength);
            }
            else
            {
                stringFormatter = new StringFormatter();
            }
            InjectDependencies(stringFormatter);
            SetFormatters(typeof(string), stringFormatter, stringFormatter);

            //
            // Type formatter is the basis for all complex objects,
            // It is special and has its own caching system (so no wrapping in a ReferenceFormatter)
            var typeFormatter = new TypeFormatter(this);

            var runtimeType = GetType().GetType();

            SetFormatters(typeof(Type), typeFormatter, typeFormatter);
            SetFormatters(runtimeType, typeFormatter, typeFormatter);

            // MemberInfos (FieldInfo, RuntimeFieldInfo, ...)
            _resolvers.Add(new ReflectionFormatterResolver(this));

            // DynamicObjectResolver is a special case, so it is not in the resolver-list
            // That is because we only want to have specific resolvers in the resolvers-list
            _dynamicResolver = new DynamicObjectFormatterResolver(this);

            // System.Linq.Expressions - mostly handled by special configurations and DynamicFormatter, but there are some special cases.
            _resolvers.Add(new ExpressionFormatterResolver());


            //
            // Basic setup is done
            // Now calculate the protocol checksum
            _knownTypes = Config.KnownTypes.ToArray();
            if (Config.KnownTypes.Distinct().Count() != _knownTypes.Length)
            {
                // We want a *good* error message. Simply saying "can't contain any type multiple times" is not enough.
                // We have to figure out which types are there more than once.
                HashSet <Type> hashSet         = new HashSet <Type>();
                List <Type>    foundDuplicates = new List <Type>();

                for (int i = 0; i < _knownTypes.Length; i++)
                {
                    var t = _knownTypes[i];
                    if (!hashSet.Add(t))
                    {
                        if (!foundDuplicates.Contains(t))
                        {
                            foundDuplicates.Add(t);
                        }
                    }
                }

                var duplicateTypesStr = string.Join(", ", foundDuplicates.Select(t => t.Name));

                throw new Exception("KnownTypes can not contain any type multiple times! Your KnownTypes collection contains the following types more than once: " + duplicateTypesStr);
            }

            //
            // Generate checksum
            {
                foreach (var t in _knownTypes)
                {
                    ProtocolChecksum.Add(t.FullName);

                    if (t.IsEnum)
                    {
                        // Enums are a special case, they are classes internally, but they only have one field ("__value")
                        // We're always serializing them in binary with their underlying type, so there's no reason changes like Adding/Removing/Renaming
                        // enum-members could ever cause any binary incompatibility
                        //
                        // A change in the base-type however WILL cause problems!
                        ProtocolChecksum.Add(t.GetEnumUnderlyingType().FullName);
                        continue;
                    }

                    if (!t.ContainsGenericParameters)
                    {
                        var meta = GetTypeMetaData(t);
                        if (meta.PrimarySchema != null)
                        {
                            foreach (var m in meta.PrimarySchema.Members)
                            {
                                ProtocolChecksum.Add(m.MemberType.FullName);
                                ProtocolChecksum.Add(m.MemberName);

                                foreach (var a in m.MemberInfo.GetCustomAttributes(true))
                                {
                                    ProtocolChecksum.Add(a.ToString());
                                }
                            }
                        }
                    }
                }

                ProtocolChecksum.Finish();
            }

            //
            // We can already pre-warm formatters
            // - dynamic serializers generate their code
            // - reference formatters generate their wrappers
            foreach (var t in _knownTypes)
            {
                if (!t.ContainsGenericParameters)
                {
                    GetReferenceFormatter(t);
                }
            }



            //
            // Finally we need "instance data"
            _instanceDataPool = new FactoryPool <InstanceData>(p =>
            {
                var d                    = new InstanceData();
                d.CurrentRoot            = null;
                d.ObjectCache            = new ObjectCache();
                d.TypeCache              = new TypeCache(_knownTypes);
                d.EncounteredSchemaTypes = new HashSet <Type>();

                return(d);
            });
            InstanceData = _instanceDataPool.RentObject();

            if (Config.Advanced.SealTypesWhenUsingKnownTypes)
            {
                if (_knownTypes.Length > 0)
                {
                    typeFormatter.Seal();
                }
            }
        }