private static void AppendGenericTypeArguments(this StringBuilder sb, Type[] genericTypeArguments)
 {
     sb.Append('[');
     for (int i = 0; i < genericTypeArguments.Length; i++)
     {
         if (i != 0)
         {
             sb.Append(',');
         }
         sb.AppendSerializationString(genericTypeArguments[i], withinGenericTypeArgument: true);
     }
     sb.Append(']');
 }
        //
        // Generate the "Signature2" binary serialization string for PropertyInfos
        //
        // Because the string is effectively a file format for serialized Reflection objects, it must be exactly correct. If missing
        // metadata prevents generating the string, this method throws a MissingMetadata exception.
        //
        public static string SerializationToString(this PropertyInfo property)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendSerializationString(property.PropertyType);
            sb.Append(' ');
            sb.Append(property.Name);
            ParameterInfo[] parameters = property.GetIndexParameters();
            if (parameters.Length != 0)
            {
                sb.Append(" [");
                sb.AppendParameters(parameters, isVarArg: false);
                sb.Append(']');
            }
            return(sb.ToString());
        }
        //
        // Generated the Signature2 substring for the parameters of a method, constructor or property.
        //
        private static void AppendParameters(this StringBuilder sb, ParameterInfo[] parameters, bool isVarArg)
        {
            string comma = string.Empty;

            for (int i = 0; i < parameters.Length; i++)
            {
                sb.Append(comma);
                sb.AppendSerializationString(parameters[i].ParameterType, withinGenericTypeArgument: false);
                comma = ", ";
            }

            if (isVarArg)
            {
                sb.Append(comma);
                sb.Append("...");
            }
        }
        //
        // Generate the "Signature2" binary serialization string for MethodInfos
        //
        // Because the string is effectively a file format for serialized Reflection objects, it must be exactly correct. If missing
        // metadata prevents generating the string, this method throws a MissingMetadata exception.
        //
        public static string SerializationToString(this MethodInfo method)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendSerializationString(method.ReturnType);
            sb.Append(' ');
            sb.Append(method.Name);
            if (method.IsGenericMethod)
            {
                // Method is a generic method definition or a constructed generic method. Either way, the emit the generic parameters or arguments in brackets.
                sb.AppendGenericTypeArguments(method.GetGenericArguments());
            }
            sb.Append('(');
            sb.AppendParameters(method.GetParametersNoCopy(), method.CallingConvention == CallingConventions.VarArgs);
            sb.Append(')');
            return(sb.ToString());
        }
        //
        // Generate the "Signature2" binary serialization string for Type objects appearing inside serialized MethodBase and PropertyInfo signatures.
        //
        // Because the string is effectively a file format for serialized Reflection objects, it must be exactly correct. If missing
        // metadata prevents generating the string, this method throws a MissingMetadata exception.
        //
        // "withinGenericArgument" is used to track whether we're in the middle of serializing a generic type argument.
        // Some of the string-generation rules change when in that state:
        //
        //    - Generic type parameters no longer get prepended with "!" or "!!".
        //    - Plain old types are serialized as assembly-qualified names enclosed in square brackets.
        //
        private static void AppendSerializationString(this StringBuilder sb, Type type, bool withinGenericTypeArgument = false)
        {
            if (type.HasElementType)
            {
                sb.AppendSerializationString(type.GetElementType(), withinGenericTypeArgument);
                if (type.IsSzArray)
                {
                    sb.Append("[]");
                }
                else if (type.IsArray)
                {
                    int rank = type.GetArrayRank();
                    if (rank == 1)
                    {
                        sb.Append('*');
                    }
                    else
                    {
                        sb.Append('[');
                        sb.Append(',', rank - 1);
                        sb.Append(']');
                    }
                }
                else if (type.IsByRef)
                {
                    sb.Append('&');
                }
                else if (type.IsPointer)
                {
                    sb.Append('*');
                }
                else
                {
                    Debug.Fail("Should not get here.");
                    throw new InvalidOperationException(); //Unexpected error: Runtime Reflection is a trusted source so we should not have gotten here.
                }
            }
            else if (type.IsGenericParameter)
            {
                if (!withinGenericTypeArgument)
                {
                    // This special rule causes generic type variables ("T") to serialize as "!T" (variable on type) or "!!T" (variable on method)
                    // to distinguish them from a plain old type named "T".
                    //
                    // This rule does not kick in if we're serializing a type variable embedded inside a generic type argument list. (Fortunately, there
                    // is no risk of ambiguity in that case because generic type argument lists always serialize plain old types as "[<assembly-qualified-name>]".)
                    sb.Append('!');
                    if (type.DeclaringMethod != null)
                    {
                        sb.Append('!');
                    }
                }
                sb.Append(type.Name);
            }
            else
            {
                // If we got here, "type" is either a plain old type or a constructed generic type.

                if (withinGenericTypeArgument)
                {
                    sb.Append('[');
                }

                Type   plainOldType;
                Type[] instantiation;
                SplitIntoPlainOldTypeAndInstantiation(type, out plainOldType, out instantiation);
                sb.Append(plainOldType.FullName);
                if (instantiation != null)
                {
                    sb.AppendGenericTypeArguments(instantiation);
                }
                if (withinGenericTypeArgument)
                {
                    sb.Append(", ");
                    sb.Append(plainOldType.Assembly.FullName);
                    sb.Append(']');
                }
            }
        }