private SerializerPair CreateCustomScalar()

in rd-net/RdFramework.Reflection/ScalarSerializer.cs [96:189]


    private SerializerPair CreateCustomScalar<T>(ISerializersSource serializers)
    {
      if (Mode.IsAssertion)
        ReflectionSerializerVerifier.AssertValidScalar(typeof(T).GetTypeInfo());
      TypeInfo typeInfo = typeof(T).GetTypeInfo();
      var allowNullable = ReflectionSerializerVerifier.CanBeNull(typeInfo);

      var memberInfos = SerializerReflectionUtil.GetSerializableFields(typeInfo);
      var memberSetters = memberInfos.Select(ReflectionUtil.GetSetter).ToArray();
      var memberGetters = memberInfos.Select(ReflectionUtil.GetGetter).ToArray();

      // todo: consider using IL emit
      CtxReadDelegate<object>[]? memberDeserializers = null;
      CtxWriteDelegate<object>[]? memberSerializers = null;

      CtxReadDelegate<T?> readerDelegate = (ctx, unsafeReader) =>
      {
        if (memberDeserializers == null)
          using (new FirstChanceExceptionInterceptor.ThreadLocalDebugInfo(typeof(T)))
            InitMemberSerializers();
        Assertion.AssertNotNull(memberDeserializers);

        if (allowNullable && !unsafeReader.ReadNullness())
          return default;

        object instance = FormatterServices.GetUninitializedObject(typeof(T));

        try
        {
          for (var index = 0; index < memberDeserializers.Length; index++)
          {
            var memberValue = memberDeserializers[index](ctx, unsafeReader);
            memberSetters[index](instance, memberValue);
          }
        }
        catch (ArgumentException e)
        {
          e.Data["Type:" + typeof(T).ToString(true)] = "";
          throw;
        }

        return (T) instance;
      };

      CtxWriteDelegate<T> writerDelegate = (ctx, unsafeWriter, value) =>
      {
        if (memberSerializers == null)
          using (new FirstChanceExceptionInterceptor.ThreadLocalDebugInfo(typeof(T)))
            InitMemberSerializers();
        Assertion.AssertNotNull(memberSerializers);

        if (allowNullable)
        {
          unsafeWriter.WriteBoolean(value != null);
          if (value == null)
            return;
        }

        try
        {
          for (var i = 0; i < memberSerializers.Length; i++)
          {
            var memberValue = memberGetters[i](value!);
            memberSerializers[i](ctx, unsafeWriter, memberValue!);
          }
        }
        catch (ArgumentException e)
        {
          e.Data["Type:" + typeof(T).ToString(true)] = "";
          throw;
        }
      };

      return new SerializerPair(readerDelegate, writerDelegate);

      // Lazy resolve cyclic depencencies and give ability to serialize tree-like structures.
      void InitMemberSerializers()
      {
        var read = new CtxReadDelegate<object>[memberInfos.Length];
        var write = new CtxWriteDelegate<object>[memberInfos.Length];

        for (var index = 0; index < memberInfos.Length; index++)
        {
          var mi = memberInfos[index];
          var returnType = ReflectionUtil.GetReturnType(mi);
          var serPair = serializers.GetOrRegisterSerializerPair(returnType, true);
          read[index] = SerializerReflectionUtil.ConvertReader<object>(serPair.Reader);
          write[index] = SerializerReflectionUtil.ConvertWriter<object>(serPair.Writer);
        }

        memberSerializers = write;
        memberDeserializers = read;
      }
    }