Reputation: 1571
I am looking for an easy way to get the reflection information on the method starting all the way from the class, and going back all the way to the declaring interface. Here is the simplified piece of code:
public interface Ix
{
void Function();
}
public class X : Ix
{
public void Function() {}
}
public class Y : X
{
}
Method info for class X has no information about Function being declared in Ix interface. Here is the call:
var info = typeof(X).GetMethod("Function");
var baseInfo = info.GetBaseDefinition()
It returns the following data:
info.DeclaringType --> MyNamespace.X
info.ReflectedType --> MyNamespace.X
baseInfo.DeclaringType --> MyNamespace.X
baseInfo.ReflectedType --> MyNamespace.X
The same information is returned for class Y.
How can I figure out that this Function was declared in interface Ix without going through all implemented interfaces and base classes of class X or Y? I could be missing something simple but I cannot figure out what. Could this be a bug?
This is .Net Core SDK version 2.1.104 and Visual Studio 2017
Upvotes: 1
Views: 1886
Reputation: 26917
Here is an extension method to retrieve the Type
s of the interfaces that a particular MethodInfo
implements in its declaring Type
:
First, a helper method to get all the interface maps for a Type
:
public static IEnumerable<InterfaceMapping> GetAllInterfaceMaps(this Type aType) =>
aType.GetTypeInfo()
.ImplementedInterfaces
.Select(ii => aType.GetInterfaceMap(ii));
Then, an extension method that uses the helper to get the interfaces for a single method:
public static Type[] GetInterfacesForMethod(this MethodInfo mi) =>
mi.ReflectedType
.GetAllInterfaceMaps()
.Where(im => im.TargetMethods.Any(tm => tm == mi))
.Select(im => im.InterfaceType)
.ToArray();
If you wanted the interfaces for all the methods of a class, you can use this instead:
public static ILookup<MethodInfo, Type> GetMethodsForInterfaces(this Type aType) =>
aType.GetAllInterfaceMaps()
.SelectMany(im => im.TargetMethods.Select(tm => new { im.TargetType, im.InterfaceType, tm }))
.ToLookup(imtm => imtm.tm, imtm => imtm.InterfaceType);
To get the interface declarations that correspond to the method, you can use this:
public static IEnumerable<MethodInfo> GetInterfaceDeclarationsForMethod(this MethodInfo mi) =>
mi.ReflectedType
.GetAllInterfaceMaps()
.SelectMany(map => Enumerable.Range(0, map.TargetMethods.Length)
.Where(n => map.TargetMethods[n] == mi)
.Select(n => map.InterfaceMethods[n]));
Upvotes: 6
Reputation: 40818
You seem to want a reverse lookup of the information that can be obtained from GetInterfaceMap. Building this is pretty straightforward,
public static Dictionary<MethodInfo, List<(Type, MethodInfo)>> GetReverseInterfaceMap(Type t)
{
var reverseMap = new Dictionary<MethodInfo, List<(Type, MethodInfo)>>();
var maps = t.GetInterfaces().ToDictionary(i => i, i => t.GetInterfaceMap(i));
foreach (var m in t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
{
var list = new List<(Type, MethodInfo)>();
foreach (var (i, map) in maps)
{
for (int index = 0; index < map.TargetMethods.Length; index++)
{
var targetMethod = map.TargetMethods[index];
if (targetMethod == m)
{
list.Add((map.InterfaceType, map.InterfaceMethods[index]));
break;
}
}
}
reverseMap[m] = list;
}
return reverseMap;
}
There are a few points to be made about how interfaces work because the picture is not as simple as going Derived -> Base -> Interface. There is a mapping which describes the concrete method each interface method calls. This information is readily available because the runtime needs it to implement interface dispatch. However the reverse is not as simple. One concrete method might be a mapping for multiple interfaces. Also it is possible for interface methods to be remapped by derived classes. This all needs to be taken into account.
Upvotes: 1