Печать полной сигнатуры метода из метода.
Существуют ли в .NET BCL какие-либо функциональные возможности для печати полной подписи метода во время выполнения (например, что вы увидите в Visual Studio ObjectBrowser - включая имена параметров) с использованием информации, доступной из MethodInfo?
Так, например, если вы посмотрите на String.Compare(), одна из перегрузок будет выглядеть так:
public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase, System.Globalization.CultureInfo culture)
Обратите внимание на наличие полной подписи со всеми квалификаторами доступа и области действия, а также полного списка параметров, включая имена. Это то, что я ищу. Я мог бы написать свой собственный метод, но я бы предпочел использовать существующую реализацию, если это возможно.
Ответы
Ответ 1
К сожалению, я не верю, что есть встроенный метод, который бы это сделал. Лучше всего было бы создать свою собственную подпись, исследуя класс MethodInfo
EDIT: я просто сделал это
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 Compare (String strA, Int32 indexA, String strB, Int32 indexB, Int32 длина, StringComparison comparisonType)
Ответ 2
Обновление от 22.03.2008
Я переписал код, добавил несколько тестов и загрузил его на GitHub
Ответ
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 для генерации перегрузок для всех методов расширения Queryable
и Enumerable
Linq.
Ответ 3
Оформить метод GetParameters() на базе MethodBase. Это даст вам информацию о параметрах, включая имя параметра. Я не верю, что существует существующий метод для печати имени, но с использованием 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();
}