// // mono-api-info.cs - Dumps public assembly information to an xml file. // // Authors: // Gonzalo Paniagua Javier (gonzalo@ximian.com) // // Copyright (C) 2003-2005 Novell, Inc (http://www.novell.com) // using System; using System.Collections; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; using System.Security.Permissions; using System.Text; using System.Xml; namespace Mono.AssemblyInfo { class Driver { #if false static int Main (string [] args) { if (args.Length == 0) return 1; AssemblyCollection acoll = new AssemblyCollection (); foreach (string fullName in args) { acoll.Add (fullName); } XmlDocument doc = new XmlDocument (); acoll.Document = doc; acoll.DoOutput (); XmlTextWriter writer = new XmlTextWriter (Console.Out); writer.Formatting = Formatting.Indented; XmlNode decl = doc.CreateXmlDeclaration ("1.0", null, null); doc.InsertBefore (decl, doc.DocumentElement); doc.WriteTo (writer); return 0; } #endif } public class AssemblyCollection { XmlDocument document; ArrayList assemblies; public AssemblyCollection () { assemblies = new ArrayList (); } public bool Add (string name) { Assembly ass = LoadAssembly (name); if (ass == null) return false; assemblies.Add (ass); return true; } public void DoOutput () { if (document == null) throw new InvalidOperationException ("Document not set"); XmlNode nassemblies = document.CreateElement ("assemblies", null); document.AppendChild (nassemblies); foreach (Assembly a in assemblies) { AssemblyData data = new AssemblyData (document, nassemblies, a); data.DoOutput (); } } public XmlDocument Document { set { document = value; } } static Assembly LoadAssembly (string aname) { Assembly ass = null; try { string name = aname; if (!name.EndsWith (".dll")) name += ".dll"; ass = Assembly.LoadFrom (name); return ass; } catch { } try { ass = Assembly.LoadWithPartialName (aname); return ass; } catch { } return null; } } abstract class BaseData { protected XmlDocument document; protected XmlNode parent; protected BaseData (XmlDocument doc, XmlNode parent) { this.document = doc; this.parent = parent; } public abstract void DoOutput (); protected void AddAttribute (XmlNode node, string name, string value) { XmlAttribute attr = document.CreateAttribute (name); attr.Value = value; node.Attributes.Append (attr); } } class AssemblyData : BaseData { Assembly ass; public AssemblyData (XmlDocument document, XmlNode parent, Assembly ass) : base (document, parent) { this.ass = ass; } public override void DoOutput () { if (document == null) throw new InvalidOperationException ("Document not set"); XmlNode nassembly = document.CreateElement ("assembly", null); AssemblyName aname = ass.GetName (); AddAttribute (nassembly, "name", aname.Name); AddAttribute (nassembly, "version", aname.Version.ToString ()); parent.AppendChild (nassembly); AttributeData.OutputAttributes (document, nassembly, ass.GetCustomAttributes (false)); Type [] types = ass.GetExportedTypes (); if (types == null || types.Length == 0) return; Array.Sort (types, TypeComparer.Default); XmlNode nss = document.CreateElement ("namespaces", null); nassembly.AppendChild (nss); string currentNS = "$%&$&"; XmlNode ns = null; XmlNode classes = null; foreach (Type t in types) { if (t.Namespace == null || t.Namespace == "") continue; if (t.IsNotPublic) continue; if (t.IsNestedPublic || t.IsNestedAssembly || t.IsNestedFamANDAssem || t.IsNestedFamORAssem || t.IsNestedPrivate) continue; if (t.DeclaringType != null) continue; // enforce !nested if (t.Namespace != currentNS) { currentNS = t.Namespace; ns = document.CreateElement ("namespace", null); AddAttribute (ns, "name", currentNS); nss.AppendChild (ns); classes = document.CreateElement ("classes", null); ns.AppendChild (classes); } TypeData bd = new TypeData (document, classes, t); bd.DoOutput (); } } } abstract class MemberData : BaseData { MemberInfo [] members; public MemberData (XmlDocument document, XmlNode parent, MemberInfo [] members) : base (document, parent) { this.members = members; } public override void DoOutput () { XmlNode mclass = document.CreateElement (ParentTag, null); parent.AppendChild (mclass); foreach (MemberInfo member in members) { XmlNode mnode = document.CreateElement (Tag, null); mclass.AppendChild (mnode); AddAttribute (mnode, "name", GetName (member)); if (!NoMemberAttributes) AddAttribute (mnode, "attrib", GetMemberAttributes (member)); AttributeData.OutputAttributes (document, mnode, member.GetCustomAttributes (false)); AddExtraData (mnode, member); } } protected virtual void AddExtraData (XmlNode p, MemberInfo member) { } protected virtual string GetName (MemberInfo member) { return "NoNAME"; } protected virtual string GetMemberAttributes (MemberInfo member) { return null; } public virtual bool NoMemberAttributes { get { return false; } set {} } public virtual string ParentTag { get { return "NoPARENTTAG"; } } public virtual string Tag { get { return "NoTAG"; } } } class TypeData : MemberData { Type type; const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic; public TypeData (XmlDocument document, XmlNode parent, Type type) : base (document, parent, null) { this.type = type; } public override void DoOutput () { if (document == null) throw new InvalidOperationException ("Document not set"); XmlNode nclass = document.CreateElement ("class", null); AddAttribute (nclass, "name", type.Name); string classType = GetClassType (type); AddAttribute (nclass, "type", classType); if (type.BaseType != null) AddAttribute (nclass, "base", type.BaseType.ToString ()); if (type.IsSealed) AddAttribute (nclass, "sealed", "true"); if (type.IsAbstract) AddAttribute (nclass, "abstract", "true"); if (type.IsSerializable) AddAttribute (nclass, "serializable", "true"); string charSet = GetCharSet (type); AddAttribute (nclass, "charset", charSet); string layout = GetLayout (type); if (layout != null) AddAttribute (nclass, "layout", layout); parent.AppendChild (nclass); AttributeData.OutputAttributes (document, nclass, type.GetCustomAttributes (false)); Type [] interfaces = type.GetInterfaces (); if (interfaces != null && interfaces.Length > 0) { XmlNode ifaces = document.CreateElement ("interfaces", null); nclass.AppendChild (ifaces); foreach (Type t in interfaces) { if (!t.IsPublic) { // we're only interested in public interfaces continue; } XmlNode iface = document.CreateElement ("interface", null); AddAttribute (iface, "name", t.ToString ()); ifaces.AppendChild (iface); } } #if NET_2_0 // Generic constraints Type [] gargs = type.GetGenericArguments (); XmlElement ngeneric = (gargs.Length == 0) ? null : document.CreateElement ("generic-type-constraints"); foreach (Type garg in gargs) { Type [] csts = garg.GetGenericParameterConstraints (); if (csts.Length == 0 || csts [0] == typeof (object)) continue; XmlElement el = document.CreateElement ("generic-type-constraint"); el.SetAttribute ("name", garg.ToString ()); el.SetAttribute ("generic-attribute", garg.GenericParameterAttributes.ToString ()); ngeneric.AppendChild (el); foreach (Type ct in csts) { XmlElement cel = document.CreateElement ("type"); cel.AppendChild (document.CreateTextNode (ct.FullName)); el.AppendChild (cel); } } if (ngeneric != null && ngeneric.FirstChild != null) nclass.AppendChild (ngeneric); #endif ArrayList members = new ArrayList (); FieldInfo[] fields = GetFields (type); if (fields.Length > 0) { Array.Sort (fields, MemberInfoComparer.Default); FieldData fd = new FieldData (document, nclass, fields); // Special case for enum fields if (classType == "enum") { string etype = fields [0].GetType ().ToString (); AddAttribute (nclass, "enumtype", etype); } members.Add (fd); } ConstructorInfo [] ctors = GetConstructors (type); if (ctors.Length > 0) { Array.Sort (ctors, MemberInfoComparer.Default); members.Add (new ConstructorData (document, nclass, ctors)); } PropertyInfo[] properties = GetProperties (type); if (properties.Length > 0) { Array.Sort (properties, MemberInfoComparer.Default); members.Add (new PropertyData (document, nclass, properties)); } EventInfo [] events = GetEvents (type); if (events.Length > 0) { Array.Sort (events, MemberInfoComparer.Default); members.Add (new EventData (document, nclass, events)); } MethodInfo [] methods = GetMethods (type); if (methods.Length > 0) { Array.Sort (methods, MemberInfoComparer.Default); members.Add (new MethodData (document, nclass, methods)); } foreach (MemberData md in members) md.DoOutput (); Type [] nested = type.GetNestedTypes (); if (nested != null && nested.Length > 0) { XmlNode classes = document.CreateElement ("classes", null); nclass.AppendChild (classes); foreach (Type t in nested) { TypeData td = new TypeData (document, classes, t); td.DoOutput (); } } } protected override string GetMemberAttributes (MemberInfo member) { if (member != type) throw new InvalidOperationException ("odd"); return ((int) type.Attributes).ToString (CultureInfo.InvariantCulture); } public static bool MustDocumentMethod(MethodBase method) { // All other methods return (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly); } static string GetClassType (Type t) { if (t.IsEnum) return "enum"; if (t.IsValueType) return "struct"; if (t.IsInterface) return "interface"; if (typeof (Delegate).IsAssignableFrom (t)) return "delegate"; return "class"; } private static string GetCharSet (Type type) { if (type.IsAnsiClass) return CharSet.Ansi.ToString (CultureInfo.InvariantCulture); if (type.IsAutoClass) return CharSet.Auto.ToString (CultureInfo.InvariantCulture); if (type.IsUnicodeClass) return CharSet.Unicode.ToString (CultureInfo.InvariantCulture); return CharSet.None.ToString (CultureInfo.InvariantCulture); } private static string GetLayout (Type type) { if (type.IsAutoLayout) return LayoutKind.Auto.ToString (CultureInfo.InvariantCulture); if (type.IsExplicitLayout) return LayoutKind.Explicit.ToString (CultureInfo.InvariantCulture); if (type.IsLayoutSequential) return LayoutKind.Sequential.ToString (CultureInfo.InvariantCulture); return null; } private FieldInfo[] GetFields (Type type) { ArrayList list = new ArrayList (); FieldInfo[] fields = type.GetFields (flags); foreach (FieldInfo field in fields) { if (field.IsSpecialName) continue; // we're only interested in public or protected members if (!field.IsPublic && !field.IsFamily && !field.IsFamilyOrAssembly) continue; list.Add (field); } return (FieldInfo[]) list.ToArray (typeof (FieldInfo)); } internal static PropertyInfo[] GetProperties (Type type) { ArrayList list = new ArrayList (); PropertyInfo[] properties = type.GetProperties (flags); foreach (PropertyInfo property in properties) { MethodInfo getMethod = null; MethodInfo setMethod = null; if (property.CanRead) { try { getMethod = property.GetGetMethod (true); } catch (System.Security.SecurityException) { } } if (property.CanWrite) { try { setMethod = property.GetSetMethod (true); } catch (System.Security.SecurityException) { } } bool hasGetter = (getMethod != null) && MustDocumentMethod (getMethod); bool hasSetter = (setMethod != null) && MustDocumentMethod (setMethod); // if neither the getter or setter should be documented, then // skip the property if (!hasGetter && !hasSetter) { continue; } list.Add (property); } return (PropertyInfo[]) list.ToArray (typeof (PropertyInfo)); } private MethodInfo[] GetMethods (Type type) { ArrayList list = new ArrayList (); MethodInfo[] methods = type.GetMethods (flags); foreach (MethodInfo method in methods) { if (method.IsSpecialName) continue; // we're only interested in public or protected members if (!MustDocumentMethod(method)) continue; list.Add (method); } return (MethodInfo[]) list.ToArray (typeof (MethodInfo)); } private ConstructorInfo[] GetConstructors (Type type) { ArrayList list = new ArrayList (); ConstructorInfo[] ctors = type.GetConstructors (flags); foreach (ConstructorInfo constructor in ctors) { // we're only interested in public or protected members if (!constructor.IsPublic && !constructor.IsFamily && !constructor.IsFamilyOrAssembly) continue; list.Add (constructor); } return (ConstructorInfo[]) list.ToArray (typeof (ConstructorInfo)); } private EventInfo[] GetEvents (Type type) { ArrayList list = new ArrayList (); EventInfo[] events = type.GetEvents (flags); foreach (EventInfo eventInfo in events) { MethodInfo addMethod = eventInfo.GetAddMethod (true); if (addMethod == null || !MustDocumentMethod (addMethod)) continue; list.Add (eventInfo); } return (EventInfo[]) list.ToArray (typeof (EventInfo)); } } class FieldData : MemberData { public FieldData (XmlDocument document, XmlNode parent, FieldInfo [] members) : base (document, parent, members) { } protected override string GetName (MemberInfo member) { FieldInfo field = (FieldInfo) member; return field.Name; } protected override string GetMemberAttributes (MemberInfo member) { FieldInfo field = (FieldInfo) member; return ((int) field.Attributes).ToString (CultureInfo.InvariantCulture); } protected override void AddExtraData (XmlNode p, MemberInfo member) { base.AddExtraData (p, member); FieldInfo field = (FieldInfo) member; AddAttribute (p, "fieldtype", field.FieldType.ToString ()); if (field.IsLiteral) { object value = field.GetValue (null); string stringValue = null; if (value is Enum) { // FIXME: when Mono bug #60090 has been // fixed, we should just be able to use // Convert.ToString stringValue = ((Enum) value).ToString ("D", CultureInfo.InvariantCulture); } else { stringValue = Convert.ToString (value, CultureInfo.InvariantCulture); } if (stringValue != null) AddAttribute (p, "value", stringValue); } } public override string ParentTag { get { return "fields"; } } public override string Tag { get { return "field"; } } } class PropertyData : MemberData { public PropertyData (XmlDocument document, XmlNode parent, PropertyInfo [] members) : base (document, parent, members) { } protected override string GetName (MemberInfo member) { PropertyInfo prop = (PropertyInfo) member; return prop.Name; } protected override void AddExtraData (XmlNode p, MemberInfo member) { base.AddExtraData (p, member); PropertyInfo prop = (PropertyInfo) member; Type t = prop.PropertyType; AddAttribute (p, "ptype", prop.PropertyType.ToString ()); MethodInfo _get = prop.GetGetMethod (true); MethodInfo _set = prop.GetSetMethod (true); bool haveGet = (_get != null && TypeData.MustDocumentMethod(_get)); bool haveSet = (_set != null && TypeData.MustDocumentMethod(_set)); MethodInfo [] methods; if (haveGet && haveSet) { methods = new MethodInfo [] {_get, _set}; } else if (haveGet) { methods = new MethodInfo [] {_get}; } else if (haveSet) { methods = new MethodInfo [] {_set}; } else { //odd return; } string parms = Parameters.GetSignature (methods [0].GetParameters ()); AddAttribute (p, "params", parms); MethodData data = new MethodData (document, p, methods); //data.NoMemberAttributes = true; data.DoOutput (); } protected override string GetMemberAttributes (MemberInfo member) { PropertyInfo prop = (PropertyInfo) member; return ((int) prop.Attributes & (0xFFFFFFFF ^ (int) PropertyAttributes.ReservedMask)).ToString (CultureInfo.InvariantCulture); } public override string ParentTag { get { return "properties"; } } public override string Tag { get { return "property"; } } } class EventData : MemberData { public EventData (XmlDocument document, XmlNode parent, EventInfo [] members) : base (document, parent, members) { } protected override string GetName (MemberInfo member) { EventInfo evt = (EventInfo) member; return evt.Name; } protected override string GetMemberAttributes (MemberInfo member) { EventInfo evt = (EventInfo) member; return ((int) evt.Attributes).ToString (CultureInfo.InvariantCulture); } protected override void AddExtraData (XmlNode p, MemberInfo member) { base.AddExtraData (p, member); EventInfo evt = (EventInfo) member; AddAttribute (p, "eventtype", evt.EventHandlerType.ToString ()); } public override string ParentTag { get { return "events"; } } public override string Tag { get { return "event"; } } } class MethodData : MemberData { bool noAtts; public MethodData (XmlDocument document, XmlNode parent, MethodBase [] members) : base (document, parent, members) { } protected override string GetName (MemberInfo member) { MethodBase method = (MethodBase) member; string name = method.Name; string parms = Parameters.GetSignature (method.GetParameters ()); #if NET_2_0 MethodInfo mi = method as MethodInfo; Type [] genArgs = mi == null ? Type.EmptyTypes : mi.GetGenericArguments (); if (genArgs.Length > 0) { string [] genArgNames = new string [genArgs.Length]; for (int i = 0; i < genArgs.Length; i++) { genArgNames [i] = genArgs [i].Name; string genArgCsts = String.Empty; Type [] gcs = genArgs [i].GetGenericParameterConstraints (); if (gcs.Length > 0) { string [] gcNames = new string [gcs.Length]; for (int g = 0; g < gcs.Length; g++) gcNames [g] = gcs [g].FullName; genArgCsts = String.Concat ( "(", string.Join (", ", gcNames), ") ", genArgNames [i]); } else genArgCsts = genArgNames [i]; if ((genArgs [i].GenericParameterAttributes & GenericParameterAttributes.ReferenceTypeConstraint) != 0) genArgCsts = "class " + genArgCsts; else if ((genArgs [i].GenericParameterAttributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) genArgCsts = "struct " + genArgCsts; genArgNames [i] = genArgCsts; } return String.Format ("{0}<{2}>({1})", name, parms, string.Join (",", genArgNames)); } #endif return String.Format ("{0}({1})", name, parms); } protected override string GetMemberAttributes (MemberInfo member) { MethodBase method = (MethodBase) member; return ((int)( method.Attributes & ~MethodAttributes.ReservedMask)).ToString (CultureInfo.InvariantCulture); } protected override void AddExtraData (XmlNode p, MemberInfo member) { base.AddExtraData (p, member); ParameterData parms = new ParameterData (document, p, ((MethodBase) member).GetParameters ()); parms.DoOutput (); if (!(member is MethodBase)) return; MethodBase mbase = (MethodBase) member; if (mbase.IsAbstract) AddAttribute (p, "abstract", "true"); if (mbase.IsVirtual) AddAttribute (p, "virtual", "true"); if (mbase.IsStatic) AddAttribute (p, "static", "true"); if (!(member is MethodInfo)) return; MethodInfo method = (MethodInfo) member; AddAttribute (p, "returntype", method.ReturnType.ToString ()); AttributeData.OutputAttributes (document, p, method.ReturnTypeCustomAttributes.GetCustomAttributes (false)); #if NET_2_0 // Generic constraints Type [] gargs = method.GetGenericArguments (); XmlElement ngeneric = (gargs.Length == 0) ? null : document.CreateElement ("generic-method-constraints"); foreach (Type garg in gargs) { Type [] csts = garg.GetGenericParameterConstraints (); if (csts.Length == 0 || csts [0] == typeof (object)) continue; XmlElement el = document.CreateElement ("generic-method-constraint"); el.SetAttribute ("name", garg.ToString ()); el.SetAttribute ("generic-attribute", garg.GenericParameterAttributes.ToString ()); ngeneric.AppendChild (el); foreach (Type ct in csts) { XmlElement cel = document.CreateElement ("type"); cel.AppendChild (document.CreateTextNode (ct.FullName)); el.AppendChild (cel); } } if (ngeneric != null && ngeneric.FirstChild != null) p.AppendChild (ngeneric); #endif } public override bool NoMemberAttributes { get { return noAtts; } set { noAtts = value; } } public override string ParentTag { get { return "methods"; } } public override string Tag { get { return "method"; } } } class ConstructorData : MethodData { public ConstructorData (XmlDocument document, XmlNode parent, ConstructorInfo [] members) : base (document, parent, members) { } public override string ParentTag { get { return "constructors"; } } public override string Tag { get { return "constructor"; } } } class ParameterData : BaseData { private ParameterInfo[] parameters; public ParameterData (XmlDocument document, XmlNode parent, ParameterInfo[] parameters) : base (document, parent) { this.parameters = parameters; } public override void DoOutput () { XmlNode parametersNode = document.CreateElement ("parameters", null); parent.AppendChild (parametersNode); foreach (ParameterInfo parameter in parameters) { XmlNode paramNode = document.CreateElement ("parameter", null); parametersNode.AppendChild (paramNode); AddAttribute (paramNode, "name", parameter.Name); AddAttribute (paramNode, "position", parameter.Position.ToString(CultureInfo.InvariantCulture)); AddAttribute (paramNode, "attrib", ((int) parameter.Attributes).ToString()); string direction = "in"; if (parameter.ParameterType.IsByRef) { direction = parameter.IsOut ? "out" : "ref"; } Type t = parameter.ParameterType; AddAttribute (paramNode, "type", t.ToString ()); if (parameter.IsOptional) { AddAttribute (paramNode, "optional", "true"); if (parameter.DefaultValue != System.DBNull.Value) AddAttribute (paramNode, "defaultValue", (parameter.DefaultValue == null) ? "NULL" : parameter.DefaultValue.ToString ()); } if (direction != "in") AddAttribute (paramNode, "direction", direction); AttributeData.OutputAttributes (document, paramNode, parameter.GetCustomAttributes (false)); } } } class AttributeData : BaseData { object [] atts; AttributeData (XmlDocument doc, XmlNode parent, object[] attributes) : base (doc, parent) { atts = attributes; } public override void DoOutput () { if (document == null) throw new InvalidOperationException ("Document not set"); if (atts == null || atts.Length == 0) return; XmlNode natts = parent.SelectSingleNode("attributes"); if (natts == null) { natts = document.CreateElement ("attributes", null); parent.AppendChild (natts); } for (int i = 0; i < atts.Length; ++i) { Type t = atts [i].GetType (); if (!t.IsPublic && !t.Name.EndsWith ("TODOAttribute")) continue; // we ignore attributes that inherit from SecurityAttribute on purpose as they: // * aren't part of GetCustomAttributes in Fx 1.0/1.1; // * are encoded differently and in a different metadata table; and // * won't ever exactly match MS implementation (from a syntax pov) if (t.IsSubclassOf (typeof (SecurityAttribute))) continue; XmlNode node = document.CreateElement ("attribute"); AddAttribute (node, "name", t.ToString ()); XmlNode properties = null; foreach (PropertyInfo pi in TypeData.GetProperties (t)) { if (pi.Name == "TypeId") continue; if (properties == null) { properties = node.AppendChild (document.CreateElement ("properties")); } try { object o = pi.GetValue (atts [i], null); XmlNode n = properties.AppendChild (document.CreateElement ("property")); AddAttribute (n, "name", pi.Name); if (o == null) { AddAttribute (n, "null", "true"); continue; } string value = o.ToString (); if (t == typeof (GuidAttribute)) value = value.ToUpper (); AddAttribute (n, "value", value); } catch (TargetInvocationException) { continue; } } natts.AppendChild (node); } } public static void OutputAttributes (XmlDocument doc, XmlNode parent, object[] attributes) { AttributeData ad = new AttributeData (doc, parent, attributes); ad.DoOutput (); } private static bool MustDocumentAttribute (Type attributeType) { // only document MonoTODOAttribute and public attributes return attributeType.Name.EndsWith ("TODOAttribute") || attributeType.IsPublic; } } class Parameters { private Parameters () {} public static string GetSignature (ParameterInfo [] infos) { if (infos == null || infos.Length == 0) return ""; StringBuilder sb = new StringBuilder (); foreach (ParameterInfo info in infos) { string modifier; if (info.IsIn) modifier = "in "; else if (info.IsRetval) modifier = "ref "; else if (info.IsOut) modifier = "out "; else modifier = ""; string type_name = info.ParameterType.ToString ().Replace ('<', '[').Replace ('>', ']'); sb.AppendFormat ("{0}{1}, ", modifier, type_name); } sb.Length -= 2; // remove ", " return sb.ToString (); } } class TypeComparer : IComparer { public static TypeComparer Default = new TypeComparer (); public int Compare (object a, object b) { Type ta = (Type) a; Type tb = (Type) b; int result = String.Compare (ta.Namespace, tb.Namespace); if (result != 0) return result; return String.Compare (ta.Name, tb.Name); } } class MemberInfoComparer : IComparer { public static MemberInfoComparer Default = new MemberInfoComparer (); public int Compare (object a, object b) { MemberInfo ma = (MemberInfo) a; MemberInfo mb = (MemberInfo) b; return String.Compare (ma.Name, mb.Name); } } class MethodBaseComparer : IComparer { public static MethodBaseComparer Default = new MethodBaseComparer (); public int Compare (object a, object b) { MethodBase ma = (MethodBase) a; MethodBase mb = (MethodBase) b; int res = String.Compare (ma.Name, mb.Name); if (res != 0) return res; ParameterInfo [] pia = ma.GetParameters (); ParameterInfo [] pib = mb.GetParameters (); if (pia.Length != pib.Length) return pia.Length - pib.Length; string siga = Parameters.GetSignature (pia); string sigb = Parameters.GetSignature (pib); return String.Compare (siga, sigb); } } }