Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Print full signature of a method from a MethodInfo

Is there any existing functionality in the .NET BCL to print the full signature of a method at runtime (like what you'd see in the Visual Studio ObjectBrowser - including parameter names) using information available from MethodInfo?

So for example, if you look up String.Compare() one of the overloads would print as:

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

Note the presence of the complete signature with all access and scope qualifiers as well as a complete list of parameters including names. This is what I'm looking for. I could write my own method, but I would rather use an existing implementation if possible.

like image 846
LBushkin Avatar asked Aug 21 '09 14:08

LBushkin


2 Answers

Update 3/22/2018

I've rewritten the code, added a few tests, and uploaded it to GitHub

It's also available through nuget

Eltons.ReflectionKit NuGet

Answer

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

    }
}

This handles practically everything, including extension methods. Got a head start from http://www.pcreview.co.uk/forums/getting-correct-method-signature-t3660896.html.

I used it in tandum with a T4 template to generate overloads for all the Queryable and Enumerable Linq extension methods.

like image 147
Kelly Elton Avatar answered Nov 13 '22 08:11

Kelly Elton


Unfortunately I don't believe there is a built in method that would do that. Your best be would be to create your own signature by investigating the MethodInfo class

EDIT: I just did this

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

and you get

Void Main(System.String[])

This might not be what you're looking for, but it's close.

How about this

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

This is the result

Int32 Compare(String strA,Int32 indexA,String strB,Int32 indexB,Int32 length,StringComparison comparisonType)

like image 28
Stan R. Avatar answered Nov 13 '22 09:11

Stan R.