将接口的ProperyInfo与类的PropertyInfo匹配

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

MyData GetProperty(Expression<Func> 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(candidate); throw new Exception("Property not found."); } // Returns precomputed metadata MyData GetMetaData(PropertyInfo property) { ... } 

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

 var data = PropertyInfo((Employee e) => e.Name); 

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

 void MyGenericMethod(int id) where T : IEmployee { var data = PropertyInfo((T e) => e.Name); } 

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

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

 InterfaceMapping mapping = typeof(Employee).GetInterfaceMap(typeof(IEmployee)); 

现在,映射将包含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 

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

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

 ParameterExpression p = Expression.Parameter("e", typeof(T)); Expression> lambda = Expression.Lambda>( Expression.Property(p, targetProperty), p); 

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

BindingFlags.FlattenHierarchy是否有效? 如果没有,您可以总是遍历typeof(T).GetInterfaces并在每个typeof(T).GetInterfaces调用GetProperties

您需要从lambda表达式中获取成员名称,并使用reflection来使该成员脱离您给定的类型:

 public static PropertyInfo PropInfo( Expression> memberGetter) { var memberName = GetExpressionMemberName(memberGetter); return typeof(TContainer).GetProperty(memberName); } public static string GetExpressionMemberName( Expression> memberGetter) { var expressionType = memberGetter.Body.NodeType; switch (expressionType) { case ExpressionType.MemberAccess: { var memberExpr = (MemberExpression) memberGetter.Body; return memberExpr.Member.Name; } case ExpressionType.Convert: { var convertExpr = (UnaryExpression) memberGetter.Body; var memberExpr = (MemberExpression) convertExpr.Operand; return memberExpr.Member.Name; } default: throw new InvalidOperationException("Expression {0} does not represent a simple member access."); } } 

这certificate它有效:

 void Main() { Console.WriteLine( MyGenericMethod() .GetGetMethod() .Invoke( new Employee {Name = "Bill"}, new object[] {})); } public class Employee : IEmployee { public string Name {get;set;} string IEmployee.Name { get { throw new Exception(); } } } public interface IEmployee {string Name {get;}} public PropertyInfo MyGenericMethod() where T : IEmployee { return PropInfo((T e) => e.Name); } 

控制台输出:

 Bill