Correspondence of the ProperyInfo interface with the PropertyInfo class

I use a method similar to the following to get some precalculated metadata related to type properties.

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) { ... }

As expected, it works when used as follows:

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

But not when used in the following general method:

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

It fails because the declaring type propertyin the first method is now IEmployee, so the property in lambda does not match the property in the type. How can I get them to match without relying on property names? (There may be several properties with the same name if the interfaces are implemented explicitly, so p1.Name == p2.Nameit will not cut it out).

+3
3

InterfaceMapping. , GetInterfaceMap(typeof(interface)), i.e.,

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<Func<T, U>> lambda = Expression.Lambda<Func<T, U>>(
    Expression.Property(p, targetProperty), p);

, , .

+6

BindingFlags.FlattenHierarchy? , typeof(T).GetInterfaces GetProperties .

0

You will need to get the member name from the lambda expression and use reflection to get this element from the type that it gave you:

public static PropertyInfo PropInfo<TContainer, TMember>(
    Expression<Func<TContainer, TMember>> memberGetter)
{
    var memberName = GetExpressionMemberName(memberGetter);
    return typeof(TContainer).GetProperty(memberName);
}

public static string GetExpressionMemberName<TContainer, TMember>(
    Expression<Func<TContainer, TMember>> 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.");
    }
}

It has been proven that it works:

void Main()
{
    Console.WriteLine(
        MyGenericMethod<Employee>()
            .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<T>() where T : IEmployee
{
    return PropInfo((T e) => e.Name);
}

Console output:

Bill
0
source

Source: https://habr.com/ru/post/1755396/


All Articles