Albero di espressioni C # per chiamare il metodo get della proprietà della classe di base

.net-4.5 c# expression-trees reflection

Domanda

Ho creato un albero di espressioni che ricava i parametri da una classe e li invoca per impostare valori in un'altra classe. Funziona molto bene per la maggior parte dei tipi, ma fallisce su un tipo derivato. Se ho:

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

e

public class B : A
{

}

Se utilizzo questo codice Albero espressioni (dove value è l'istanza derivata):

// 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() )
}

Riceverò questa eccezione:

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

Cosa sto facendo di sbagliato qui?

Risposta accettata

È necessario utilizzare la proprietà stessa e non il metodo Getter per questo:

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

Fondamentalmente è necessario utilizzare questo metodo Property invece di questo metodo Property sovraccarico

AGGIORNARE

Per approccio GetGetMethod: se var objectType uguale al tipo A , allora funziona. Se è uguale al tipo B , allora non funziona.

La mia ipotesi, che il problema è in property.DeclaringType . Per entrambi i casi è di tipo A , come proprietà dichiarata in esso. Ma questo codice restituirà diversi ReflectedType per la stessa proprietà:

var reflectingTypeForA = typeof(A).GetProperty("MyString").GetGetMethod().ReflectedType;
var reflectingTypeForB = typeof(B).GetProperty("MyString").GetGetMethod().ReflectedType;

Per A restituisce A , e per B di tipo restituisce B . La mia ipotesi è che per il caso B Expression.Property logic controlla che property.DeclaringType sia A , ma GetGetMethod ha ReflectedType uguale a B , quindi pensa che sia proprietà di un altro oggetto. Non ho alcuna prova per questo, ma solo questi membri sono diversi tra due chiamate GetGetMethod

UPDATE2

Ecco un codice utilizzato da Expression.Property (metodo MethodInfo):

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; 
} 

Il problema è in questa linea:

if (method == propertyMethod) {
    return true; 
} 

Quando ottieni MethodInfo da GetGetMethod di Tipo A , è diverso da MethodInfo di Tipo B e questo controllo restituisce false. Questo esempio restituisce anche false:

var propertyAGetter = typeof(A).GetProperty("MyString").GetGetMethod();
var propertyBGetter = typeof(B).GetProperty("MyString").GetGetMethod();
bool areTheSame = propertyAGetter == propertyBGetter; // it equals to false


Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché