How to check if the MethodInfo interface is a "new" method

I am trying to use reflection to get a list of all interface methods + its underlying interfaces.

So far I have this:

var methods = type.GetMethods().Concat( type.GetInterfaces() .SelectMany(@interface => @interface.GetMethods())); 

I would like to be able to filter methods that shadow methods are declared in the base interfaces, i.e. "new" methods:

 public interface IBaseInterface { string Method(); } public interface IInterfaceWithNewMethod : IBaseInterface { new string Method(); } 

In my current code, the result includes both methods - I would like to get only IInterfaceWithMethod.Method and filter out IBaseInterface.Method .

Fiddle: https://dotnetfiddle.net/fwVeLS

PS: If this helps, you can assume that I have access to a specific instance of the derived interface. The type of this instance will be known only at runtime (this is a dynamic proxy).

+6
source share
2 answers

Well, the dirty way could be to manually check for the signature of the method.

The signature verification method might look like this:

 public static bool HasSameSignature(MethodInfo potentiallyHidingMethod, MethodInfo baseMethod) { //different name, therefore not same signature if (potentiallyHidingMethod.Name != baseMethod.Name) return false; //now we check if they have the same parameter types... var potentiallyHidingMethodParameters = potentiallyHidingMethod.GetParameters(); var baseMethodParameters = baseMethod.GetParameters(); //different number of parameters, therefore not same signature if (potentiallyHidingMethodParameters.Length != baseMethodParameters.Length) return false; for (int i = 0; i < potentiallyHidingMethodParameters.Length; i++) { //if a parameter type doesn't match, it not the same signature if (potentiallyHidingMethodParameters[i].ParameterType != baseMethodParameters[i].ParameterType) return false; } //if we've gotten this far, they have the same name and parameters, //therefore, it the same signature. return true; } 

Then you need to check the methods of the derived interface to see if they hide (or match the signature) any methods of the base interface:

 Type type = typeof(IInterfaceWithNewMethod); var potentiallyHidingMethods = type.GetMethods(); var baseTypeMethods =type.GetInterfaces() .SelectMany(@interface => @interface.GetMethods()); var hidingMethods = potentiallyHidingMethods .Where(hiding => baseTypeMethods.Any(baseMethod => HasSameSignature(hiding, baseMethod))); 

Please note that this is a bit naive implementation. I would not be surprised if there is an easier way or corner cases that this does not cover.

EDIT: Slightly misunderstood the desired result. Using the code above, you will get all the methods of the base interface, as well as the derived methods of the interface, but filtered out any methods of the base interface that were hidden using the derived interface:

 var allMethodsButFavouringHiding = potentiallyHidingMethods.Concat( baseTypeMethods.Where(baseMethod => !potentiallyHidingMethods.Any(potentiallyhiding => HasSameSignature(potentiallyhiding, baseMethod)))); 

EDITx2: I checked the following interfaces:

 public interface IBaseInterface { string BaseMethodTokeep(); string MethodToHide(); string MethodSameName(); } public interface IInterfaceWithNewMethod : IBaseInterface { new string MethodToHide(); new string MethodSameName(object butDifferentParameters); string DerivedMethodToKeep(); } 

The result is a MethodInfo collection:

 MethodToHide (IInterfaceWithNewMethod) MethodSameName (IInterfaceWithNewMethod) DerivedMethodToKeep (IInterfaceWithNewMethod) BaseMethodTokeep (IBaseInterface) MethodSameName (IBaseInterface) 

Thus, it saves any methods of the base interface that are not hidden, any derived interface methods (which are hidden or otherwise), and honors any changes to the signature (that is, various parameters that will not lead to hiding).

EDITx3: another test with overloads added:

 public interface IBaseInterface { string MethodOverloadTest(); string MethodOverloadTest(object withParam); } public interface IInterfaceWithNewMethod : IBaseInterface { new string MethodOverloadTest(); } 

With the results:

 MethodOverloadTest() for IInterfaceWithNewMethod MethodOverloadTest(object) for IBaseInterface 
+2
source

In the end, I took advantage of the combination of the answers of Chris Sinclair and Thomas Levesque.

It is a little more extensive, but it is more reliable.

More importantly, I think it is much easier to read and reason, which is the top priority when considering reflection. We all know how easy it is for a reflection code to become a complex and whole mess ...

 internal static class TypeExtensions { /// <summary> /// Gets a collection of all methods declared by the interface <paramref name="type"/> or any of its base interfaces. /// </summary> /// <param name="type">An interface type.</param> /// <returns>A collection of all methods declared by the interface <paramref name="type"/> or any of its base interfaces.</returns> public static IEnumerable<MethodInfo> GetInterfaceMethods(this Type type) { var allMethods = type.GetMethods().Concat( type.GetInterfaces() .SelectMany(@interface => @interface.GetMethods())); return allMethods.GroupBy(method => new Signature(method)) .Select(SignatureWithTheMostDerivedDeclaringType); } private static MethodInfo SignatureWithTheMostDerivedDeclaringType(IGrouping<Signature, MethodInfo> group) { return group.Aggregate( (a, b) => a.DeclaringType.IsAssignableFrom(b.DeclaringType) ? b : a); } private sealed class Signature { private readonly MethodInfo method; public Signature(MethodInfo method) { this.method = method; } public override bool Equals(object obj) { var that = obj as Signature; if (that == null) return false; //different names, therefore different signatures. if (this.method.Name != that.method.Name) return false; var thisParams = this.method.GetParameters(); var thatParams = that.method.GetParameters(); //different number of parameters, therefore different signatures if (thisParams.Length != thatParams.Length) return false; //different paramaters, therefore different signatures for (int i = 0; i < thisParams.Length; i++) if (!AreParamsEqual(thisParams[i], thatParams[i])) return false; return true; } /// <summary> /// Two parameters are equal if they have the same type and /// they're either both "out" parameters or "non-out" parameters. /// </summary> private bool AreParamsEqual(ParameterInfo x, ParameterInfo y) { return x.ParameterType == y.ParameterType && x.IsOut == y.IsOut; } public override int GetHashCode() { int hash = 37; hash = hash*23 + method.Name.GetHashCode(); foreach (var p in method.GetParameters()) { hash = hash*23 + p.ParameterType.GetHashCode(); hash = hash*23 + p.IsOut.GetHashCode(); } return hash; } } } 
+1
source

Source: https://habr.com/ru/post/974916/


All Articles