C#表达式树调用基类属性get方法

3

我已经构建了一个表达式树,从一个类中推导出参数并调用它们来设置另一个类中的值。对于大多数类型,它都非常有效,但是对于派生类型,它会失败。如果我有:

public class A
{
    public string MyString {get; set;}
}

并且

public class B : A
{

}

如果我使用这个表达式树代码(其中value是派生实例):
// Get the type of the object for caching and ExpressionTree purposes
var objectType = value.GetType();
// Define input parameter
var inputObject = Expression.Parameter( typeof( object ), "value" );

var properties = objectType.GetProperties( BindingFlags.Instance | BindingFlags.Public );
foreach (var property in properties)
{
    Expression.Property( Expression.ConvertChecked( inputObject, property.DeclaringType ), property.GetGetMethod() )
}

我会收到这个异常:

{System.ArgumentException: The method 'My.Namespace.A.get_MyString' is not a property accessor
at System.Linq.Expressions.Expression.GetProperty(MethodInfo mi)...

这里我做错了什么?
1个回答

7
您需要使用属性本身而不是Getter方法:

您需要使用属性本身而不是Getter方法:

foreach (var property in properties)
{
    Expression.Property( 
         Expression.ConvertChecked( inputObject, property.DeclaringType ),
         property);
}

基本上,您需要使用此Property方法而不是这个重载的Property方法
更新:
对于GetGetMethod方法-如果var objectType等于A类型,则可以工作。如果它等于B类型,则无法工作。
我猜问题在于property.DeclaringType。对于两种情况,它都是A类型,因为属性在其中声明。但是,对于相同的属性,此代码将返回不同的ReflectedType
var reflectingTypeForA = typeof(A).GetProperty("MyString").GetGetMethod().ReflectedType;
var reflectingTypeForB = typeof(B).GetProperty("MyString").GetGetMethod().ReflectedType;

对于类型 A,它返回 A,对于类型 B,它返回 B。我猜测对于类型 B 的情况,Expression.Property 逻辑会检查 property.DeclaringType 是否为 A,但是 GetGetMethod 具有与 B 相等的 ReflectedType,因此它认为这是另一个对象的属性。我没有任何证据证明这一点,但两个 GetGetMethod 调用之间只有这些成员不同。

更新2:

以下是由 Expression.Property(MethodInfo method) 使用的代码:

private static PropertyInfo GetProperty(MethodInfo mi)
{
    Type type = mi.DeclaringType;
    BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic;
    flags |= (mi.IsStatic) ? BindingFlags.Static : BindingFlags.Instance;
    PropertyInfo[] props = type.GetProperties(flags);
    foreach (PropertyInfo pi in props)
    {
        if (pi.CanRead && CheckMethod(mi, pi.GetGetMethod(true)))
        {
            return pi;
        }
        if (pi.CanWrite && CheckMethod(mi, pi.GetSetMethod(true)))
        {
            return pi;
        }
    }
    throw new SomeException();
}

private static bool CheckMethod(MethodInfo method, MethodInfo propertyMethod) { 
    if (method == propertyMethod) {
        return true; 
    } 
    // If the type is an interface then the handle for the method got by the compiler will not be the
    // same as that returned by reflection. 
    // Check for this condition and try and get the method from reflection.
    Type type = method.DeclaringType;
    if (type.IsInterface && method.Name == propertyMethod.Name && type.GetMethod(method.Name) == propertyMethod) {
        return true; 
    }
    return false; 
} 

问题出在这一行:
if (method == propertyMethod) {
    return true; 
} 

当您从类型中使用GetGetMethod获取MethodInfo时,与从类型中获取的MethodInfo不同,此检查将返回false。如下所示的示例也返回false:
var propertyAGetter = typeof(A).GetProperty("MyString").GetGetMethod();
var propertyBGetter = typeof(B).GetProperty("MyString").GetGetMethod();
bool areTheSame = propertyAGetter == propertyBGetter; // it equals to false

2
这个有效了,谢谢!如果你能告诉我为什么它有效,我会给你答案的,因为我不理解我的代码,这让我很痛苦! - Haney
1
added my guess into answer :) - Sergey Litvinov
1
从技术上讲,这两个对象由于ReflectedType的不同而不同,因此这不是一个错误。以下是Jon Skeet关于ReflectedType的答案 - https://dev59.com/_HHYa4cB1Zd3GeqPMYVy - Sergey Litvinov
1
我不同意他们关于“ReflectedType”的设计决定,但至少现在我明白了为什么我的代码会出问题。 :) - Haney
2
似乎Expression.Property接受一个派生的ReflectedTypePropertyInfo,但不接受一个具有派生ReflectedTypeMethodInfo,这仍然看起来像是一个错误。 - nmclean
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接