将接口的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.NonPublic
和GetGetMethod(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