/// <summary> /// Some writer configurations may perform some upfront analysis based upon the type being serialised. This method should be called by the Serialiser before it starts any /// other serialisation process. This will return a dictionary of optimised 'member setters' that the Serialiser may use (a member setter is a delegate that will write the /// field and property content for an instance - writing the ObjectStart and ObjectEnd content is the Serialiser's responsibility because that is related to reference-tracking, /// which is also the Serialiser's responsibility). Note that this dictionary may include entries that have a null value to indicate that it was not possible generate a member /// setter for that type (this may save the Serialiser from calling TryToGenerateMemberSetter for that type later on because it knows that null will be returned). When this /// method is called, it will always return a new Dictionary reference and so the caller is free to take ownership of it and mutate it if it wants to. It is not mandatory for /// the Serialiser to call this method (and it's not mandatory for it to use the returned dictionary) but it is highly recommended. This should always be called before any /// other serialisation methods, calling it after starting serialisation of data will result in an exception being thrown. /// </summary> public Dictionary <Type, Action <object> > PrepareForSerialisation(Type serialisationTargetType, ISerialisationTypeConverter[] typeConverters) { if (serialisationTargetType == null) { throw new ArgumentNullException(nameof(serialisationTargetType)); } if (typeConverters == null) { throw new ArgumentNullException(nameof(typeConverters)); } if (_haveStartedSerialising) { throw new Exception(nameof(PrepareForSerialisation) + " must be called before any other serialisation commences"); } // The "BinarySerialisationDeepCompiledMemberSetters.GetMemberSettersFor" method will allow optimised member setters to be generated for more types (which could make // the serialisation process much faster) but it may only be used if particular compromises can be made: // - Reference tracking must be disabled (this means that circular references will result in a stack overflow) // - The DefaultTypeAnalyser must be used (because the member setters are cached for reuse and they are generated using the DefaultTypeAnalyser and they may not // be applicable to cases where a different type analyser is required) // - No type converters are used (because these may change the shape of the data during the serialisation process but GetMemberSettersFor needs the data to be remain // in its original form) var typeConvertersIsFastTypeConverterArray = (typeConverters is IFastSerialisationTypeConverter[]); if ((ReferenceReuseStrategy != ReferenceReuseOptions.SpeedyButLimited) || (_deepMemberSetterCacheIfEnabled == null) || (_typeAnalyser != DefaultTypeAnalyser.Instance) || (!typeConvertersIsFastTypeConverterArray && typeConverters.Any(t => !(t is IFastSerialisationTypeConverter)))) { return(new Dictionary <Type, Action <object> >()); } var fastTypeConverters = typeConvertersIsFastTypeConverterArray ? (IFastSerialisationTypeConverter[])typeConverters : typeConverters.Cast <IFastSerialisationTypeConverter>().ToArray(); var generatedMemberSetterResult = BinarySerialisationDeepCompiledMemberSetters.GetMemberSettersFor(serialisationTargetType, fastTypeConverters, _deepMemberSetterCacheIfEnabled); foreach (var typeName in generatedMemberSetterResult.TypeNamesToDeclare) { WriteByte((byte)BinarySerialisationDataType.TypeNamePreLoad); WriteBytes(typeName.AsStringAndReferenceID); } foreach (var fieldName in generatedMemberSetterResult.FieldNamesToDeclare) { WriteByte((byte)BinarySerialisationDataType.FieldNamePreLoad); WriteBytes(fieldName.AsStringAndReferenceID); } return(generatedMemberSetterResult.MemberSetters.ToDictionary( entry => entry.Key, entry => (entry.Value == null) ? null : (Action <object>)(source => entry.Value(source, this)) )); }
/// <summary> /// This will throw a FastestTreeSerialisationNotPossibleException if the specified type does not fully support FastestTreeBinarySerialisation - this may be because /// the type is a class that is not sealed or it has a member that is a class that is not sealed or if IntPtr values exist within the object graph (which are not /// supported by this serialiser). It will only throw at the first problem because one problem can have knock on effects and so a single property of an unsupported /// property type that is deeply nested in the object graph will prevent all of the parent types getting the full-speed treatment. /// /// This method is intended for us in unit tests in code that references this library - if it is important for this fastest possible serialisation approach to be /// used for particular types then it would be sensible to have unit tests that catch any changes to those types that would prevent optimal serialisation performance /// because that change is likely to result in a performance regression. /// </summary> public static void EnsureThatTypeIsOptimalForFastestTreeSerialisation(Type type, IFastSerialisationTypeConverter[] typeConverters) { if (type == null) { throw new ArgumentNullException(nameof(type)); } if (typeConverters == null) { throw new ArgumentNullException(nameof(typeConverters)); } // If this method doesn't throw, then great! If it DOES throw then let that exception bubble up to the caller to let them know that all is not well. BinarySerialisationDeepCompiledMemberSetters.EnsureThatTypeIsOptimalForFastestTreeSerialisation(type, typeConverters); }