将接口的ProperyInfo与类的PropertyInfo匹配

Gre*_*reg 3 .net c# reflection

我使用类似于以下的方法来获取与Type的属性相关的一些预先计算的元数据.

MyData GetProperty<T, U>(Expression<Func<T, U>> member)
{
    // Get the property referenced in the lambda expression
    MemberExpression expression = member.Body as MemberExpression;
    PropertyInfo property = expression.Member as PropertyInfo;

    // get the properties in the type T
    PropertyInfo[] candidates = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

    // Find the match
    foreach (PropertyInfo candidate in candidates)
        if (candidate == property)
            return GetMetaData<T>(candidate);
    throw new Exception("Property not found.");
}

// Returns precomputed metadata
MyData GetMetaData<T>(PropertyInfo property) { ... }
Run Code Online (Sandbox Code Playgroud)

正如您所料,它的使用方式如下:

var data = PropertyInfo((Employee e) => e.Name);
Run Code Online (Sandbox Code Playgroud)

但是在以下通用方法中使用时不是:

void MyGenericMethod<T>(int id) where T : IEmployee
{
    var data = PropertyInfo((T e) => e.Name);
}
Run Code Online (Sandbox Code Playgroud)

它失败是因为property第一种方法中的声明类型现在是IEmployee,因此lambda中的属性与该类型中的属性不匹配.如何在不依赖属性名称的情况下让它们匹配?(如果明确实现接口,则可以有多个具有相同名称的属性,因此p1.Name == p2.Name不会删除它).

Rub*_*ben 6

您可能需要的是InterfaceMapping.您可以通过调用从实际类型中获取GetInterfaceMap(typeof(interface)),即

InterfaceMapping mapping = typeof(Employee).GetInterfaceMap(typeof(IEmployee));
Run Code Online (Sandbox Code Playgroud)

现在,映射将包含InterfaceMethods将包含您在反映界面时看到的方法的字段,以及TargetMethods哪些是类的实现方法.请注意,这会将getter方法从接口映射到目标类的getter方法.您需要通过将类的各种属性的getter方法映射到找到的getter方法来找到正确的接口属性.

Type interfaceType = typeof(IEmployee);
Type classType = typeof(Employee);
PropertyInfo nameProperty = interfaceType.GetProperty("Name");

MethodInfo nameGetter = nameProperty.GetGetMethod();
InterfaceMapping mapping = classType.GetInterfaceMap(interfaceType);

MethodInfo targetMethod = null;
for (int i = 0; i < mapping.InterfaceMethods.Length; i++)
{
    if (mapping.InterfaceMethods[i] == nameGetter)
    {
        targetMethod = mapping.TargetMethods[i];
        break;
    }
}

PropertyInfo targetProperty = null;
foreach (PropertyInfo property in classType.GetProperties(
    BindingFlags.Instance | BindingFlags.GetProperty | 
    BindingFlags.Public | BindingFlags.NonPublic))   // include non-public!
{
    if (targetMethod == property.GetGetMethod(true)) // include non-public!
    {
        targetProperty = property;
        break;
    }
}

// targetProperty is the actual property
Run Code Online (Sandbox Code Playgroud)

注意:请注意使用BindingFlags.NonPublicGetGetMethod(true)此处访问私有成员.如果你有一个显式的接口实现,那么实际上并没有一个匹配接口属性的公共属性,而是有一个名为Some.NameSpace.IEmployee.Name的映射的私有属性(当然,这是你的显式实现).

当您找到合适的房产时,您可以打电话

ParameterExpression p = Expression.Parameter("e", typeof(T));
Expression<Func<T, U>> lambda = Expression.Lambda<Func<T, U>>(
    Expression.Property(p, targetProperty), p);
Run Code Online (Sandbox Code Playgroud)

并且你有一个lambda表达式,它使用类的属性而不是接口的属性.