예제 #1
0
 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);
 }
예제 #2
0
        /// <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));
        }