Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>(note: shadowing = hiding = new in method/property/event definition in C#)</p> <p>No one responded, so I'll post the code I cooked in the meantime. I hate to post 400 lines of code, but I wanted to be complete. There are two main methods: <code>GetVisibleMethods</code> and <code>GetVisibleProperties</code>. They are extension methods of the <code>Type</code> class. They will return the <em>public visible</em> (non-shadowed/non-overridden) methods/properties of a type. They should even handle VB.NET assemblies (VB.NET normally uses <code>hide-by-name</code> shadowing instead of <code>hidebysig</code> as done by C#). They cache their result in two static collections (<code>Methods</code> and <code>Properties</code>). The code is for C# 4.0, so I'm using <code>ConcurrentDictionary&lt;T, U&gt;</code>. If you are using C# 3.5 you can replace it with a <code>Dictionary&lt;T, U&gt;</code> but you have to protect it with a <code>lock</code> () { } when you read from it and when you write to it.</p> <p>How does it works? It works by recursion (I know that recursion is normally bad, but I hope no one will create a 1.000 level inheritance chain). </p> <p>For "real" types (non-interfaces) it walks upward one level (using recursion, so this one level up could go one level up and so on) and, from the returned list of methods/properties, it removes the methods/properties it's overloading/hiding.</p> <p>For interfaces it's a little more complex. <code>Type.GetInterfaces()</code> returns all the interfaces that are inherited, ignoring if they are directly inherited or indirectly inherited. For each of those interfaces a list of declared methods/properties is calculated (through recursion). This list is paired with a list of methods/properties that are hidden by the interface (the <code>HashSet&lt;MethodInfo&gt;</code>/<code>HashSet&lt;PropertyInfo&gt;</code>). These methods/properties hidden by one interface or another are removed from all the other methods/properties returned from interfaces (so that if you have <code>I1</code> with <code>Method1(int)</code>, <code>I2</code> inheriting from <code>I1</code> that redeclares <code>Method1(int)</code> and in doing so hides <code>I1.Method1</code> and <code>I3</code> inheriting from <code>I1</code> and <code>I2</code>, the fact that <code>I2</code> hides <code>I1.Method1</code> will be applied to the methods returned from exploring <code>I1</code>, so removing <code>I1.Method1(int)</code> (this happens because I don't generate an inheritance map for interfaces, I simply look at what hides what)). </p> <p>From the returned collections of methods/properties one can use Linq to find the looked for method/property. Note that with interfaces you could find more than one method/property with a given signature. An example:</p> <pre><code>interface I1 { void Method1(); } interface I2 { void Method1(); } interface I3 : I1, I2 { } </code></pre> <p><code>I3</code> will return two <code>Method1()</code>.</p> <p>The code:</p> <pre><code>using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; public static class TypeEx { /// &lt;summary&gt; /// Type, Tuple&amp;lt;Methods of type, (for interfaces)methods of base interfaces shadowed&amp;gt; /// &lt;/summary&gt; public static readonly ConcurrentDictionary&lt;Type, Tuple&lt;MethodInfo[], HashSet&lt;MethodInfo&gt;&gt;&gt; Methods = new ConcurrentDictionary&lt;Type, Tuple&lt;MethodInfo[], HashSet&lt;MethodInfo&gt;&gt;&gt;(); /// &lt;summary&gt; /// Type, Tuple&amp;lt;Properties of type, (for interfaces)properties of base interfaces shadowed&amp;gt; /// &lt;/summary&gt; public static readonly ConcurrentDictionary&lt;Type, Tuple&lt;PropertyInfo[], HashSet&lt;PropertyInfo&gt;&gt;&gt; Properties = new ConcurrentDictionary&lt;Type, Tuple&lt;PropertyInfo[], HashSet&lt;PropertyInfo&gt;&gt;&gt;(); public static MethodInfo[] GetVisibleMethods(this Type type) { if (type.IsInterface) { return (MethodInfo[])type.GetVisibleMethodsInterfaceImpl().Item1.Clone(); } return (MethodInfo[])type.GetVisibleMethodsImpl().Clone(); } public static PropertyInfo[] GetVisibleProperties(this Type type) { if (type.IsInterface) { return (PropertyInfo[])type.GetVisiblePropertiesInterfaceImpl().Item1.Clone(); } return (PropertyInfo[])type.GetVisiblePropertiesImpl().Clone(); } private static MethodInfo[] GetVisibleMethodsImpl(this Type type) { Tuple&lt;MethodInfo[], HashSet&lt;MethodInfo&gt;&gt; tuple; if (Methods.TryGetValue(type, out tuple)) { return tuple.Item1; } var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); if (type.BaseType == null) { Methods.TryAdd(type, Tuple.Create(methods, (HashSet&lt;MethodInfo&gt;)null)); return methods; } var baseMethods = type.BaseType.GetVisibleMethodsImpl().ToList(); foreach (var method in methods) { if (method.IsHideByName()) { baseMethods.RemoveAll(p =&gt; p.Name == method.Name); } else { int numGenericArguments = method.GetGenericArguments().Length; var parameters = method.GetParameters(); baseMethods.RemoveAll(p =&gt; { if (!method.EqualSignature(numGenericArguments, parameters, p)) { return false; } return true; }); } } if (baseMethods.Count == 0) { Methods.TryAdd(type, Tuple.Create(methods, (HashSet&lt;MethodInfo&gt;)null)); return methods; } var methods3 = new MethodInfo[methods.Length + baseMethods.Count]; Array.Copy(methods, 0, methods3, 0, methods.Length); baseMethods.CopyTo(methods3, methods.Length); Methods.TryAdd(type, Tuple.Create(methods3, (HashSet&lt;MethodInfo&gt;)null)); return methods3; } private static Tuple&lt;MethodInfo[], HashSet&lt;MethodInfo&gt;&gt; GetVisibleMethodsInterfaceImpl(this Type type) { Tuple&lt;MethodInfo[], HashSet&lt;MethodInfo&gt;&gt; tuple; if (Methods.TryGetValue(type, out tuple)) { return tuple; } var methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); var baseInterfaces = type.GetInterfaces(); if (baseInterfaces.Length == 0) { tuple = Tuple.Create(methods, new HashSet&lt;MethodInfo&gt;()); Methods.TryAdd(type, tuple); return tuple; } var baseMethods = new List&lt;MethodInfo&gt;(); var baseMethodsTemp = new MethodInfo[baseInterfaces.Length][]; var shadowedMethods = new HashSet&lt;MethodInfo&gt;(); for (int i = 0; i &lt; baseInterfaces.Length; i++) { var tuple2 = baseInterfaces[i].GetVisibleMethodsInterfaceImpl(); baseMethodsTemp[i] = tuple2.Item1; shadowedMethods.UnionWith(tuple2.Item2); } for (int i = 0; i &lt; baseInterfaces.Length; i++) { baseMethods.AddRange(baseMethodsTemp[i].Where(p =&gt; !shadowedMethods.Contains(p))); } foreach (var method in methods) { if (method.IsHideByName()) { baseMethods.RemoveAll(p =&gt; { if (p.Name == method.Name) { shadowedMethods.Add(p); return true; } return false; }); } else { int numGenericArguments = method.GetGenericArguments().Length; var parameters = method.GetParameters(); baseMethods.RemoveAll(p =&gt; { if (!method.EqualSignature(numGenericArguments, parameters, p)) { return false; } shadowedMethods.Add(p); return true; }); } } if (baseMethods.Count == 0) { tuple = Tuple.Create(methods, shadowedMethods); Methods.TryAdd(type, tuple); return tuple; } var methods3 = new MethodInfo[methods.Length + baseMethods.Count]; Array.Copy(methods, 0, methods3, 0, methods.Length); baseMethods.CopyTo(methods3, methods.Length); tuple = Tuple.Create(methods3, shadowedMethods); Methods.TryAdd(type, tuple); return tuple; } private static PropertyInfo[] GetVisiblePropertiesImpl(this Type type) { Tuple&lt;PropertyInfo[], HashSet&lt;PropertyInfo&gt;&gt; tuple; if (Properties.TryGetValue(type, out tuple)) { return tuple.Item1; } var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); if (type.BaseType == null) { Properties.TryAdd(type, Tuple.Create(properties, (HashSet&lt;PropertyInfo&gt;)null)); return properties; } var baseProperties = type.BaseType.GetVisiblePropertiesImpl().ToList(); foreach (var property in properties) { if (property.IsHideByName()) { baseProperties.RemoveAll(p =&gt; p.Name == property.Name); } else { var indexers = property.GetIndexParameters(); baseProperties.RemoveAll(p =&gt; { if (!property.EqualSignature(indexers, p)) { return false; } return true; }); } } if (baseProperties.Count == 0) { Properties.TryAdd(type, Tuple.Create(properties, (HashSet&lt;PropertyInfo&gt;)null)); return properties; } var properties3 = new PropertyInfo[properties.Length + baseProperties.Count]; Array.Copy(properties, 0, properties3, 0, properties.Length); baseProperties.CopyTo(properties3, properties.Length); Properties.TryAdd(type, Tuple.Create(properties3, (HashSet&lt;PropertyInfo&gt;)null)); return properties3; } private static Tuple&lt;PropertyInfo[], HashSet&lt;PropertyInfo&gt;&gt; GetVisiblePropertiesInterfaceImpl(this Type type) { Tuple&lt;PropertyInfo[], HashSet&lt;PropertyInfo&gt;&gt; tuple; if (Properties.TryGetValue(type, out tuple)) { return tuple; } var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); var baseInterfaces = type.GetInterfaces(); if (baseInterfaces.Length == 0) { tuple = Tuple.Create(properties, new HashSet&lt;PropertyInfo&gt;()); Properties.TryAdd(type, tuple); return tuple; } var baseProperties = new List&lt;PropertyInfo&gt;(); var basePropertiesTemp = new PropertyInfo[baseInterfaces.Length][]; var shadowedProperties = new HashSet&lt;PropertyInfo&gt;(); for (int i = 0; i &lt; baseInterfaces.Length; i++) { var tuple2 = baseInterfaces[i].GetVisiblePropertiesInterfaceImpl(); basePropertiesTemp[i] = tuple2.Item1; shadowedProperties.UnionWith(tuple2.Item2); } for (int i = 0; i &lt; baseInterfaces.Length; i++) { baseProperties.AddRange(basePropertiesTemp[i].Where(p =&gt; !shadowedProperties.Contains(p))); } foreach (var property in properties) { if (property.IsHideByName()) { baseProperties.RemoveAll(p =&gt; { if (p.Name == property.Name) { shadowedProperties.Add(p); return true; } return false; }); } else { var indexers = property.GetIndexParameters(); baseProperties.RemoveAll(p =&gt; { if (!property.EqualSignature(indexers, p)) { return false; } shadowedProperties.Add(p); return true; }); } } if (baseProperties.Count == 0) { tuple = Tuple.Create(properties, shadowedProperties); Properties.TryAdd(type, tuple); return tuple; } var properties3 = new PropertyInfo[properties.Length + baseProperties.Count]; Array.Copy(properties, 0, properties3, 0, properties.Length); baseProperties.CopyTo(properties3, properties.Length); tuple = Tuple.Create(properties3, shadowedProperties); Properties.TryAdd(type, tuple); return tuple; } private static bool EqualSignature(this MethodInfo method1, int numGenericArguments1, ParameterInfo[] parameters1, MethodInfo method2) { // To shadow by signature a method must have same name, same number of // generic arguments, same number of parameters and same parameters' type if (method1.Name != method2.Name) { return false; } if (numGenericArguments1 != method2.GetGenericArguments().Length) { return false; } var parameters2 = method2.GetParameters(); if (!parameters1.EqualParameterTypes(parameters2)) { return false; } return true; } private static bool EqualSignature(this PropertyInfo property1, ParameterInfo[] indexers1, PropertyInfo property2) { // To shadow by signature a property must have same name, // same number of indexers and same indexers' type if (property1.Name != property2.Name) { return false; } var parameters2 = property1.GetIndexParameters(); if (!indexers1.EqualParameterTypes(parameters2)) { return false; } return true; } private static bool EqualParameterTypes(this ParameterInfo[] parameters1, ParameterInfo[] parameters2) { if (parameters1.Length != parameters2.Length) { return false; } for (int i = 0; i &lt; parameters1.Length; i++) { if (parameters1[i].IsOut != parameters2[i].IsOut) { return false; } if (parameters1[i].ParameterType.IsGenericParameter) { if (!parameters2[i].ParameterType.IsGenericParameter) { return false; } if (parameters1[i].ParameterType.GenericParameterPosition != parameters2[i].ParameterType.GenericParameterPosition) { return false; } } else if (parameters1[i].ParameterType != parameters2[i].ParameterType) { return false; } } return true; } private static bool IsHideByName(this MethodInfo method) { if (!method.Attributes.HasFlag(MethodAttributes.HideBySig) &amp;&amp; (!method.Attributes.HasFlag(MethodAttributes.Virtual) || method.Attributes.HasFlag(MethodAttributes.NewSlot))) { return true; } return false; } private static bool IsHideByName(this PropertyInfo property) { var get = property.GetGetMethod(); if (get != null &amp;&amp; get.IsHideByName()) { return true; } var set = property.GetSetMethod(); if (set != null &amp;&amp; set.IsHideByName()) { return true; } return false; } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload