【问题标题】:Print full signature of a method from a MethodInfo从 MethodInfo 打印方法的完整签名
【发布时间】:2009-08-21 14:05:38
【问题描述】:

.NET BCL 中是否有任何现有功能可以使用 MethodInfo 提供的信息在运行时打印方法的完整签名(如您在 Visual Studio ObjectBrowser 中看到的 - 包括参数名称)?

例如,如果您查找 String.Compare(),其中一个重载将打印为:

public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase, System.Globalization.CultureInfo culture)

请注意包含所有访问和范围限定符的完整签名以及包括名称在内的完整参数列表。这就是我要找的。我可以编写自己的方法,但如果可能的话,我宁愿使用现有的实现。

【问题讨论】:

    标签: c# .net debugging reflection


    【解决方案1】:

    2018 年 3 月 22 日更新

    我重写了代码,添加了一些测试,uploaded it to GitHub

    也可以通过 nuget 获得

    Eltons.ReflectionKit

    回答

    using System.Text;
    
    namespace System.Reflection
    {
        public static class MethodInfoExtensions
        {
            /// <summary>
            /// Return the method signature as a string.
            /// </summary>
            /// <param name="method">The Method</param>
            /// <param name="callable">Return as an callable string(public void a(string b) would return a(b))</param>
            /// <returns>Method signature</returns>
            public static string GetSignature(this MethodInfo method, bool callable = false)
            {
                var firstParam = true;
                var sigBuilder = new StringBuilder();
                if (callable == false)
                {
                    if (method.IsPublic)
                        sigBuilder.Append("public ");
                    else if (method.IsPrivate)
                        sigBuilder.Append("private ");
                    else if (method.IsAssembly)
                        sigBuilder.Append("internal ");
                    if (method.IsFamily)
                        sigBuilder.Append("protected ");
                    if (method.IsStatic)
                        sigBuilder.Append("static ");
                    sigBuilder.Append(TypeName(method.ReturnType));
                    sigBuilder.Append(' ');
                }
                sigBuilder.Append(method.Name);
    
                // Add method generics
                if(method.IsGenericMethod)
                {
                    sigBuilder.Append("<");
                    foreach(var g in method.GetGenericArguments())
                    {
                        if (firstParam)
                            firstParam = false;
                        else
                            sigBuilder.Append(", ");
                        sigBuilder.Append(TypeName(g));
                    }
                    sigBuilder.Append(">");
                }
                sigBuilder.Append("(");
                firstParam = true;
                var secondParam = false;
                foreach (var param in method.GetParameters())
                {
                    if (firstParam)
                    {
                        firstParam = false;
                        if (method.IsDefined(typeof(System.Runtime.CompilerServices.ExtensionAttribute), false))
                        {
                            if (callable)
                            {
                                secondParam = true;
                                continue;
                            }
                            sigBuilder.Append("this ");
                        }
                    }
                    else if (secondParam == true)
                        secondParam = false;
                    else
                        sigBuilder.Append(", ");
                    if (param.ParameterType.IsByRef)
                        sigBuilder.Append("ref ");
                    else if (param.IsOut)
                        sigBuilder.Append("out ");
                    if (!callable)
                    {
                        sigBuilder.Append(TypeName(param.ParameterType));
                        sigBuilder.Append(' ');
                    }
                    sigBuilder.Append(param.Name);
                }
                sigBuilder.Append(")");
                return sigBuilder.ToString();
            }
    
            /// <summary>
            /// Get full type name with full namespace names
            /// </summary>
            /// <param name="type">Type. May be generic or nullable</param>
            /// <returns>Full type name, fully qualified namespaces</returns>
            public static string TypeName(Type type)
            {
                var nullableType = Nullable.GetUnderlyingType(type);
                if (nullableType != null)
                    return nullableType.Name + "?";
    
                if (!(type.IsGenericType && type.Name.Contains('`')))
                    switch (type.Name)
                    {
                        case "String": return "string";
                        case "Int32": return "int";
                        case "Decimal": return "decimal";
                        case "Object": return "object";
                        case "Void": return "void";
                        default:
                            {
                                return string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName;
                            }
                    }
    
                var sb = new StringBuilder(type.Name.Substring(0,
                type.Name.IndexOf('`'))
                );
                sb.Append('<');
                var first = true;
                foreach (var t in type.GetGenericArguments())
                {
                    if (!first)
                        sb.Append(',');
                    sb.Append(TypeName(t));
                    first = false;
                }
                sb.Append('>');
                return sb.ToString();
            }
    
        }
    }
    

    这几乎可以处理所有事情,包括扩展方法。从http://www.pcreview.co.uk/forums/getting-correct-method-signature-t3660896.html 抢占先机。

    我将它与 T4 模板结合使用,为所有 QueryableEnumerable Linq 扩展方法生成重载。

    【讨论】:

    • 谢谢你...为我节省了很多时间。我对它进行了更多处理,以处理参数数组、可选参数和通用约束以及属性。仍然不是 100% 完成,但我想我会把它传递下去。 gist.github.com/4476307
    • @jamietre 不错,我把它分叉了,我得试试看。
    • 谢谢,这也适用于 Generik 方法和 Generic 参数
    • @JamieTreworgy 这确实非常好!您是否考虑过为其编写一些单元测试并在 Nuget 上发布?
    • 效果很好!我对代码做了一个小的编辑——对于泛型类型下的嵌套类型,IsGenericType 是真的,但名称不包含向后的 ' 字符,这会导致 Substring 调用中的异常。
    【解决方案2】:

    不幸的是,我不相信有内置的方法可以做到这一点。您最好通过调查 MethodInfo 类来创建自己的签名

    编辑:我刚刚这样做了

     MethodBase mi = MethodInfo.GetCurrentMethod();
     mi.ToString();
    

    你得到

    Void Main(System.String[])

    这可能不是您想要的,但很接近。

    这个怎么样

    public static class MethodInfoExtension
    {
        public static string MethodSignature(this MethodInfo mi)
        {
            String[] param = mi.GetParameters()
                               .Select(p => String.Format("{0} {1}",p.ParameterType.Name,p.Name))
                               .ToArray();
    
            string signature = String.Format("{0} {1}({2})", mi.ReturnType.Name, mi.Name, String.Join(",", param));
    
            return signature;
        }
    }
    
    var methods = typeof(string).GetMethods().Where( x => x.Name.Equals("Compare"));
        
    foreach(MethodInfo item in methods)
    {
        Console.WriteLine(item.MethodSignature());
    }
    

    这是结果

    Int32 比较(字符串 strA,Int32 indexA,String strB,Int32 indexB,Int32 长度,字符串比较 比较类型)

    【讨论】:

    • 谢谢。不幸的是,我的用例确实需要方法的参数名称,而 MethodInfo.ToString() 不会发出。
    【解决方案3】:

    检查 MethodBase 上的 GetParameters() 方法。这将为您提供有关参数的信息,包括参数的名称。我不相信现有的方法可以打印出名称,但使用 ParameterInfo[] 来构建应该是微不足道的。

    这个怎么样:

    public string GetSignature(MethodInfo mi)
    {
      if(mi == null)
        return "";
      StringBuilder sb = new StringBuilder();
    
      if(mi.IsPrivate)
        sb.Append("private ");
      else if(mi.IsPublic)
        sb.Append("public ");
      if(mi.IsAbstract)
        sb.Append("abstract ");
      if(mi.IsStatic)
        sb.Append("static ");
      if(mi.IsVirtual)
        sb.Append("virtual ");
    
      sb.Append(mi.ReturnType.Name + " ");
    
      sb.Append(mi.Name + "(");
    
      String[] param = mi.GetParameters()
        .Select(p => String.Format(
                  "{0} {1}",p.ParameterType.Name,p.Name))
                              .ToArray();
    
    
      sb.Append(String.Join(", ",param));
    
      sb.Append(")");
    
      return sb.ToString();
    }
    

    【讨论】:

      【解决方案4】:

      如果方法有参数,ParameterInfo.Member.ToString() 会输出你想要的。如果没有,创建签名非常容易。这个方法会返回你想要的:

      public string PrintSignature(MethodInfo methodInfo)
      {
          ParameterInfo[] parameters = methodInfo.GetParameters();
          return parameters.Length > 0 ? parameters[0].Member.ToString() : $"{methodInfo.ReturnType.ToString()} {methodInfo.Name}()";
      }
      

      如果你也想要访问限定符:

      public string GetSignatureSansModifiers(MethodInfo methodInfo)
      {
          ParameterInfo[] parameters = methodInfo.GetParameters();
          return parameters.Length > 0 ? parameters[0].Member.ToString() : $"{methodInfo.ReturnType.ToString()} {methodInfo.Name}()";
      }
      
      public string PrintSignature(MethodInfo methodInfo)
      {
          StringBuilder sb = new StringBuilder();
      
          if (methodInfo.IsPrivate)
              sb.Append("private ");
          else if (methodInfo.IsPublic)
              sb.Append("public ");
          if (methodInfo.IsAbstract)
              sb.Append("abstract ");
          if (methodInfo.IsStatic)
              sb.Append("static ");
          if (methodInfo.IsVirtual)
              sb.Append("virtual ");
          sb.Append(GetSignatureSansModifiers(methodInfo));
          return sb.ToString();
      }
      

      【讨论】:

        猜你喜欢
        • 2022-06-21
        • 2017-03-17
        • 1970-01-01
        • 2016-05-27
        • 1970-01-01
        • 2011-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多