2009-08-21 10 views
25

Czy istnieje jakaś funkcjonalność w .NET BCL, aby wydrukować pełny podpis metody w środowisku wykonawczym (np. Co zobaczysz w Visual Studio ObjectBrowser - w tym nazwy parametrów), używając informacje dostępne z MethodInfo?Wydrukuj pełną sygnaturę metody z MethodInfo

Tak na przykład, jeśli spojrzeć w górę String.Compare() jeden z przeciążeniami by wydrukować jako:

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

zauważyć obecność pełnego podpisu ze wszystkimi dostępu i zakres kwalifikacyjnych, jak również kompletne lista parametrów, w tym nazwy. Tego właśnie szukam. Mógłbym napisać własną metodę, ale wolałbym raczej użyć istniejącej implementacji, jeśli to możliwe.

Odpowiedz

24

Niestety nie sądzę, że istnieje wbudowana metoda, która by to zrobiła. Twoja najlepiej byłoby, aby stworzyć swój własny podpis badając klasa MethodInfo

EDIT: Właśnie to zrobił

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

i masz

void main (System.String [ ])

To może nie być to, czego szukasz, ale jest blisko.

Jak o tym

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()); 
    } 

Jest to wynik

Int32 Porównaj (String Stra, Int32 indexA, String Strb, Int32 indexB, Int32 długość, StringComparison comparisonType)

+0

Dzięki. Niestety, mój przypadek użycia wymaga nazw parametrów do metody, która MethodInfo.ToString() nie emituje. – LBushkin

+1

tak również nie wyświetla atrybutów metody. –

0

Zamówienie metody GetParameters() w MethodBase. Otrzymasz informacje o parametrach, w tym nazwę parametru. Nie sądzę, istnieje istniejąca metoda drukowania nazwy, ale przy użyciu ParameterInfo [] do budowy, które powinny być trywialne.

Jak o tym:

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(); 
} 
24
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(); 
     } 

    } 
} 

ten obsługuje praktycznie wszystko, w tym metod rozszerzenie. Masz szansę zacząć od http://www.pcreview.co.uk/forums/getting-correct-method-signature-t3660896.html.

Użyłem go w tandum z szablonem T4 do generowania przeciążenia dla wszystkich metod rozszerzenia Linq Queryable i Enumerable.

+10

Dzięki za to ... zaoszczędziło mi to dużo czasu. Zbudowałem go trochę więcej, aby obsługiwać tablice param, parametry opcjonalne i ogólne ograniczenia, a także właściwości. Nadal nie jest w 100% kompletny, ale pomyślałem, że będę go mijał. https://gist.github.com/4476307 –

+1

@jamietre Nice, rozwidlałem to, będę musiał się z tym bawić. –

+1

dziękuję, to działa dla metod Generik i parametrów generycznych też –

Powiązane problemy