net - what can be done with reflection in c#
Imprimir firma completa de un método de un MethodInfo (3)
Actualización 22/03/2018
Reescribí el código, agregué algunas pruebas y lo cargué en GitHub
Responder
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();
}
}
}
Esto maneja prácticamente todo, incluidos los métodos de extensión. Tengo una ventaja desde http://www.pcreview.co.uk/forums/getting-correct-method-signature-t3660896.html .
Lo usé en tandum con una plantilla T4 para generar sobrecargas para todos los métodos de extensión Queryable
y Enumerable
Linq.
¿Hay alguna funcionalidad existente en .NET BCL para imprimir la firma completa de un método en tiempo de ejecución (como lo que vería en Visual Studio ObjectBrowser, incluidos los nombres de los parámetros) utilizando la información disponible de MethodInfo?
Entonces, por ejemplo, si busca String.Compare (), una de las sobrecargas se imprimiría como:
public static int Compare(string strA, int indexA, string strB, int indexB, int length, bool ignoreCase, System.Globalization.CultureInfo culture)
Tenga en cuenta la presencia de la firma completa con todos los calificadores de acceso y alcance, así como una lista completa de parámetros, incluidos los nombres. Esto es lo que estoy buscando. Podría escribir mi propio método, pero preferiría usar una implementación existente si fuera posible.
Lamentablemente, no creo que haya un método integrado que pueda hacer eso. Lo mejor que puede hacer es crear su propia firma investigando la clase MethodInfo
EDITAR: Acabo de hacer esto
MethodBase mi = MethodInfo.GetCurrentMethod();
mi.ToString();
y obtienes
Void Main (System.String [])
Puede que esto no sea lo que estás buscando, pero está cerca.
Qué tal esto
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());
}
Este es el resultado
Int32 Compare (String strA, Int32 indexA, String strB, Int32 indexB, Int32 length, StringComparison comparisonType)
Verifica el método GetParameters () en MethodBase. Eso le dará la información sobre los parámetros, incluido el nombre del parámetro. No creo que exista un método preexistente para imprimir el nombre pero usando ParameterInfo [] para compilar que debería ser trivial.
Qué tal esto:
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();
}