private static SerializableMember Create(TypeInfo beingSerializedType, string name, MethodInfo getter, FieldInfo field, MethodInfo formatter, MethodInfo shouldSerialize, bool emitDefaultValue)
        {
            if (beingSerializedType == null)
            {
                Throw.ArgumentNullException(nameof(beingSerializedType));
            }

            if (name == null)
            {
                Throw.ArgumentNullException(nameof(name));
            }

            if (field == null && getter == null)
            {
                Throw.InvalidOperationException($"At least one of {nameof(field)} and {nameof(getter)} must be non-null");
            }

            if (field != null && getter != null)
            {
                Throw.InvalidOperationException($"Only one of {nameof(field)} and {nameof(getter)} can be non-null");
            }

            if (formatter == null)
            {
                Throw.ArgumentNullException(nameof(formatter));
            }

            TypeInfo toSerializeType;

            // getter can be an instance method or a static method
            //   if it's a static method, it can take 0 or 1 parameters
            //      the 1 parameter must be the type to be serialized, or something it is assignable to
            //   if it's an instance method, it can only take 0 parameters
            if (getter != null)
            {
                if (getter.ReturnType == Types.VoidType)
                {
                    Throw.ArgumentException($"{nameof(getter)} must return a non-void value", nameof(getter));
                }

                var getterParams = getter.GetParameters();

                if (getter.IsStatic)
                {
                    if (getterParams.Length == 0)
                    {
                        /* that's fine */
                    }
                    else if (getterParams.Length == 1)
                    {
                        var takenParam = getterParams[0].ParameterType.GetTypeInfo();
                        if (!takenParam.IsAssignableFrom(beingSerializedType))
                        {
                            Throw.ArgumentException($"{getter}'s single parameter must be assignable from {beingSerializedType}", nameof(getter));
                        }
                    }
                    else
                    {
                        Throw.ArgumentException($"Since {getter} is a static method, it cannot take more than 1 parameter", nameof(getter));
                    }
                }
                else
                {
                    if (getterParams.Length > 0)
                    {
                        Throw.ArgumentException($"Since {getter} is an instance method, it cannot take any parameters", nameof(getter));
                    }
                }

                toSerializeType = getter.ReturnType.GetTypeInfo();
            }
            else
            {
                toSerializeType = field.FieldType.GetTypeInfo();
            }

            // formatter needs to take the toSerializeType (or a type it's assignable to)
            //   and an IBufferWriter<char>
            //   and a WriteContext
            //   and return bool (false indicates insufficient space was available)
            {
                if (!formatter.IsStatic)
                {
                    Throw.ArgumentException($"{nameof(formatter)} must be a static method", nameof(formatter));
                }

                var formatterRetType = formatter.ReturnType.GetTypeInfo();
                if (formatterRetType != Types.BoolType)
                {
                    Throw.ArgumentException($"{nameof(formatter)} must return bool", nameof(formatter));
                }

                var args = formatter.GetParameters();
                if (args.Length != 3)
                {
                    Throw.ArgumentException($"{nameof(formatter)} must take 3 parameters", nameof(formatter));
                }

                if (!args[0].ParameterType.IsAssignableFrom(toSerializeType))
                {
                    Throw.ArgumentException($"The first paramater to {nameof(formatter)} must be accept a {toSerializeType}", nameof(formatter));
                }

                var p2 = args[1].ParameterType.GetTypeInfo();
                if (!p2.IsByRef)
                {
                    Throw.ArgumentException($"The second paramater to {nameof(formatter)} must be an in {nameof(WriteContext)}, was not by ref", nameof(formatter));
                }

                if (p2.GetElementType() != Types.WriteContext)
                {
                    Throw.ArgumentException($"The second paramater to {nameof(formatter)} must be an in {nameof(WriteContext)}", nameof(formatter));
                }

                if (args[2].ParameterType.GetTypeInfo() != Types.IBufferWriterOfCharType)
                {
                    Throw.ArgumentException($"The third paramater to {nameof(formatter)} must be a {nameof(IBufferWriter<char>)}", nameof(formatter));
                }
            }

            var shouldSerializeOnType = (getter?.DeclaringType ?? field?.DeclaringType).GetTypeInfo();

            CheckShouldSerializeMethod(shouldSerialize, shouldSerializeOnType);

            return(new SerializableMember(name, getter, field, formatter, shouldSerialize, emitDefaultValue));
        }
        private static DeserializableMember Create(TypeInfo beingDeserializedType, string name, MethodInfo setter, FieldInfo field, MethodInfo parser, bool isRequired, MethodInfo reset)
        {
            if (beingDeserializedType == null)
            {
                Throw.ArgumentNullException(nameof(beingDeserializedType));
            }

            if (name == null)
            {
                Throw.ArgumentNullException(nameof(name));
            }

            if (field == null && setter == null)
            {
                Throw.InvalidOperationException($"At least one of {nameof(field)} and {nameof(setter)} must be non-null");
            }

            if (field != null && setter != null)
            {
                Throw.InvalidOperationException($"Only one of {nameof(field)} and {nameof(setter)} can be non-null");
            }

            if (parser == null)
            {
                Throw.ArgumentNullException(nameof(parser));
            }

            if (name.Length == 0)
            {
                Throw.ArgumentException($"{nameof(name)} must be at least 1 character long", nameof(name));
            }

            TypeInfo valueType;

            // setter must take single parameter (the result of parser)
            //   can be instance or static
            //   and cannot return a value
            // -- OR --
            // setter must take two parameters,
            //    the first is the record value
            //    the second is the value (the result of parser)
            //    cannot return a value
            //    and must be static
            if (setter != null)
            {
                var args = setter.GetParameters();
                if (args.Length == 1)
                {
                    valueType = args[0].ParameterType.GetTypeInfo();

                    var returnsNoValue = setter.ReturnType == Types.VoidType;

                    if (!returnsNoValue)
                    {
                        Throw.ArgumentException($"{nameof(setter)} must not return a value", nameof(setter));
                    }
                }
                else if (args.Length == 2)
                {
                    valueType = args[1].ParameterType.GetTypeInfo();

                    var returnsNoValue = setter.ReturnType == Types.VoidType;

                    if (!returnsNoValue)
                    {
                        Throw.ArgumentException($"{nameof(setter)} must not return a value", nameof(setter));
                    }

                    if (!setter.IsStatic)
                    {
                        Throw.ArgumentException($"{nameof(setter)} taking two parameters must be static", nameof(setter));
                    }
                }
                else
                {
                    Throw.ArgumentException($"{nameof(setter)} must take one or two parameters", nameof(setter));
                    return(default); // just for flow control, the above won't actually return