private static MemberSetterDetails TryToGenerateMemberSetter(Type type) { return (BinarySerialisationCompiledMemberSetters.GetMemberSetterAvailability( type, DefaultTypeAnalyser.Instance, valueWriterRetriever: t => { // These tests are only for "simple" member setters - ones where properties are of types that may be serialised using IWrite methods (such as // Boolean, String and DateTime) and not for when nested member setters are required for fields or properties of more complex types, which is // when non-null values would need to be returned from a valueWriterRetriever delegate return null; } ) .MemberSetterDetailsIfSuccessful); }
/// <summary> /// This will return a compiled 'member setter' for the specified type, if it's possible to create one. A member setter takes an instance of an object and writes the data /// for the fields and properties to the writer. It does not write the ObjectStart and ObjectEnd data since the caller takes responsibility for those because reference /// tracking is handled by the caller and it may need to inject a ReferenceID after the ObjectStart data. When reference tracking is enabled, only limited types may have /// a member setter generated for them because reference tracking is not possible for the field and property values that the member setter writes (and so the only types /// that member setters may be provided for will have fields and properties that are all primitive-like values, such as genuine primitives and strings and DateTime and /// the other types that IWrite handles and that can never result in circular references). This will return null if a member setter could not be provided for the type. /// </summary> public Action <object> TryToGenerateMemberSetter(Type type) { if (type == null) { throw new ArgumentNullException(nameof(type)); } // We want to cache the results of the member setter generation (even if it wasn't successful and resulted in a null) in a static dictionary so that the results may be // shared across multiple serialisation processes (the Serialiser class will maintain a short-lived 'member setter cache' that relates to its current serialisation request // but it will not be shared between one request and another). If we didn't do this then we might find LINQ expressions being compiled for some types for every serialisation. // The only thing to be careful about is that BinarySerialisationCompiledMemberSetters.TryToGenerateMemberSetter may vary its behaviour depending upon the implementation of // IAnalyseTypesForSerialisation used - to avoid any problems, caching is only enabled if the DefaultTypeAnalyser is used and it is only NOT used in unit tests since the // constructor that takes an IAnalyseTypesForSerialisation is internal). var allowCache = (_typeAnalyser == DefaultTypeAnalyser.Instance); if (!allowCache || !_memberSetterCacheForDefaultTypeAnalyser.TryGetValue(type, out var compiledMemberSetter)) { // The BinarySerialisationCompiledMemberSetters.TryToGenerateMemberSetter method has a facility to take existing member setters and use them for fields or properties // on the current type in order to create a more complex member setter - one that can set values other than primitive-esque data types. This would bypass any reference // tracking and is not currently enabled (so the "valueWriterRetriever" delegate always returns null). Since reference tracking is not required for struct instances, it // may seem reasonable to change this behaviour to allow forming more complex member setters for types whose members are all primitive-like OR structs but structs can // have fields that are reference types and reference-tracking IS required for those values and so more analysis would be required in order to be sure that it was safe // (structs with reference fields could form part of a circular reference loop and we need to be aware of those, which we can't be if reference tracking is not available). var memberSetterAndFieldsSet = BinarySerialisationCompiledMemberSetters.GetMemberSetterAvailability(type, _typeAnalyser, t => null).MemberSetterDetailsIfSuccessful; if (memberSetterAndFieldsSet == null) { compiledMemberSetter = null; } else { compiledMemberSetter = memberSetterAndFieldsSet.GetCompiledMemberSetter(); } if (allowCache) { _memberSetterCacheForDefaultTypeAnalyser.TryAdd(type, compiledMemberSetter); // Don't worry if the add fails, it just means another thread did the same work to produce the same result } } if (compiledMemberSetter == null) { return(null); } return(value => compiledMemberSetter(value, this)); }