Given a type, a name and a signature, how can I do a member lookup of the member with name name and signature signature using the C# rules of 7.4 (the 7.4 is the chapter number from the C# Language Specification) (or at least part of them… Let’s say I can live with an exact match, without conversions/casts) at runtime? I need to get a MethodInfo/PropertyInfo/… because then I have to use it with reflection (to be more exact I’m trying to build an Expression.ForEach builder (a factory able to create an Expression Tree that represent a foreach statement), and to be pixel-perfect with the C# foreach I have to be able to do duck-typing and search for a GetEnumerator method (in the collection), a Current property and a MoveNext method (in the enumerator), as written in 8.8.4)
The problem (an example of the problem)
class C1
{
public int Current { get; set; }
public object MoveNext()
{
return null;
}
}
class C2 : C1
{
public new long Current { get; set; }
public new bool MoveNext()
{
return true;
}
}
class C3 : C2
{
}
var m = typeof(C3).GetMethods(); // I get both versions of MoveNext()
var p = typeof(C3).GetProperties(); // I get both versions of Current
Clearly if I try typeof(C3).GetProperty("Current") I get an AmbiguousMatchException exception.
A similar but different problem is present with interfaces:
interface I0
{
int Current { get; set; }
}
interface I1 : I0
{
new long Current { get; set; }
}
interface I2 : I1, I0
{
new object Current { get; set; }
}
interface I3 : I2
{
}
Here if I try to do a typeof(I3).GetProperties() I don’t get the Current property (and this is something known, see for example GetProperties() to return all properties for an interface inheritance hierarchy), but I can’t simply flatten the interfaces, because then I wouldn’t know who is hiding who.
I know that probably this problem is solved somewhere in the Microsoft.CSharp.RuntimeBinder namespace (declared in the Microsoft.CSharp assembly). This because I’m trying to use C# rules and member lookup is necessary when you have dynamic method invocation, but I haven’t been able to find anything (and then what I would get would be an Expression or perhaps a direct invocation).
After some thought it’s clear that there is something similar in the Microsoft.VisualBasic assembly. VB.NET supports late binding. It’s in Microsoft.VisualBasic.CompilerServices.NewLateBinding, but it doesn’t expose the late bounded methods.
(note: shadowing = hiding = new in method/property/event definition in C#)
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:
GetVisibleMethodsandGetVisibleProperties. They are extension methods of theTypeclass. They will return the public visible (non-shadowed/non-overridden) methods/properties of a type. They should even handle VB.NET assemblies (VB.NET normally useshide-by-nameshadowing instead ofhidebysigas done by C#). They cache their result in two static collections (MethodsandProperties). The code is for C# 4.0, so I’m usingConcurrentDictionary<T, U>. If you are using C# 3.5 you can replace it with aDictionary<T, U>but you have to protect it with alock() { } when you read from it and when you write to it.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).
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.
For interfaces it’s a little more complex.
Type.GetInterfaces()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 (theHashSet<MethodInfo>/HashSet<PropertyInfo>). These methods/properties hidden by one interface or another are removed from all the other methods/properties returned from interfaces (so that if you haveI1withMethod1(int),I2inheriting fromI1that redeclaresMethod1(int)and in doing so hidesI1.Method1andI3inheriting fromI1andI2, the fact thatI2hidesI1.Method1will be applied to the methods returned from exploringI1, so removingI1.Method1(int)(this happens because I don’t generate an inheritance map for interfaces, I simply look at what hides what)).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:
I3will return twoMethod1().The code: