2012-02-27 9 views
8

Ho una gerarchia di classi padre/figlio dove il genitore dichiara astrattamente una proprietà di stringa e la classe bambino lo implementa:Come far dichiarare il tipo da un'espressione?

abstract class Parent 
{ 
    public abstract string Value { get; } 
} 

class Child : Parent 
{ 
    public override string Value { get { return null; } } 
} 

Quando uso un'espressione che esplicitamente (o implicitamente) utilizza la classe Bambino, mi aspetto DeclaringType le proprie espressioni di MemberInfo di essere 'Bambino', ma invece è principale:

Child child = new Child(); 
Expression<Func<string>> expression = (() => child.Value); 
MemberInfo memberInfo = expression.GetMemberInfo(); 
Assert.AreEqual(typeof(Child), memberInfo.DeclaringType); // FAILS! 

l'asserzione non riesce perché la DeclaringType è Capogruppo.

C'è qualcosa che posso fare nel dichiarare la mia espressione o consumarla per rivelare l'uso effettivo del tipo Bambino?

NOTA: GetMemberInfo() di cui sopra come un metodo di estensione:

public static class TypeExtensions 
{ 
    /// <summary> 
    /// Gets the member info represented by an expression. 
    /// </summary> 
    /// <param name="expression">The member expression.</param> 
    /// <returns>The member info represeted by the expression.</returns> 
    public static MemberInfo GetMemberInfo(this Expression expression) 
    { 
     var lambda = (LambdaExpression)expression; 

     MemberExpression memberExpression; 
     if (lambda.Body is UnaryExpression) 
     { 
      var unaryExpression = (UnaryExpression)lambda.Body; 
      memberExpression = (MemberExpression)unaryExpression.Operand; 
     } 
     else memberExpression = (MemberExpression)lambda.Body; 

     return memberExpression.Member; 
    } 
} 
+1

classe 'Child' non lo fa eredita da 'Parent'! – vulkanino

+1

Cos'è questo metodo GetMemberInfo()? Se si tratta di un'estensione, si prega di postarne l'implementazione. – Ani

+1

"Se l'oggetto Type da cui è stato ottenuto questo oggetto MemberInfo non ha dichiarato questo membro, la proprietà DeclaringType rappresenterà uno dei suoi tipi base." – vulkanino

risposta

8

No - questa è una rappresentazione accurata di ciò che viene emessa dal compilatore C# (ho anche dimenticato che avevamo scritto questo!). L'override viene effettivamente ignorato quando si cerca il membro - il compilatore si preoccupa solo del tipo che originariamente era dichiarato il membro. Puoi vederlo da solo compilando il codice e poi guardando l'IL. Questo metodo:

static void Main() 
{ 
    Child c = new Child(); 
    string x = c.Value; 
} 

viene compilata in questo IL:

IL_0000: nop 
IL_0001: newobj  instance void Child::.ctor() 
IL_0006: stloc.0 
IL_0007: ldloc.0 
IL_0008: callvirt instance string Parent::get_Value() 
IL_000d: stloc.1 
IL_000e: ret 

Un punto di curiosità: il compilatore VB non fa opera allo stesso modo, quindi questo metodo:

Public Shared Sub Main(Args As String()) 
    Dim x As Child = New Child() 
    Dim y As String = x.Value 
End Sub 

è compilato come:

IL_0000: newobj  instance void [lib]Child::.ctor() 
IL_0005: stloc.0 
IL_0006: ldloc.0 
IL_0007: callvirt instance string [lib]Child::get_Value() 
IL_000c: stloc.1 
IL_000d: ret 
+1

Wow, grazie per la risposta dettagliata! Immagino che questo significhi: "È impossibile", quindi ho bisogno di tornare al tavolo da disegno. Il mio obiettivo attuale è GetCustomAttributes(), ma alcuni degli attributi personalizzati vengono aggiunti alle proprietà sottoposte a override in sottoclassi, ma utilizzando il precedente non riesco a raggiungerli poiché DeclaringType è il genitore. – Trinition

+0

@Trinition: Ma potresti ottenere il tipo di * target * del riferimento del membro, cioè il tipo di 'child'. Non sarebbe abbastanza buono? È difficile sapere esattamente cosa mostrare perché il tuo codice di esempio non è valido - non esiste un metodo simile a "GetMemberInfo' su" Expression ". –

+0

Ho modificato e aggiunto il corpo di GetMemberInfo() sopra. – Trinition

1

Se non si desidera il metodo del tipo statico su cui si lavora, ma piuttosto l'ultimo override, è possibile. Non ho prove, ma qualcosa di simile alla seguente dovrebbe fare il lavoro:

public bool FindOverride(MethodInfo baseMethod, Type type) 
{ 
    if(baseMethod==null) 
     throw new ArgumentNullException("baseMethod"); 
    if(type==null) 
     throw new ArgumentNullException("type"); 
    if(!type.IsSubclassOf(baseMethod.ReflectedType)) 
     throw new ArgumentException(string.Format("Type must be subtype of {0}",baseMethod.DeclaringType)); 
    while(true) 
    { 
     var methods=type.GetMethods(BindingFlags.Instance| 
            BindingFlags.DeclaredOnly| 
            BindingFlags.Public| 
            BindingFlags.NonPublic); 
     var method=methods.FirstOrDefault(m=>m.GetBaseDefinition()==baseMethod)) 
     if(method!=null) 
      return method; 
     type=type.BaseType; 
    } 
} 

Dove si passa il MemberInfo come il primo param, e il tipo di esecuzione dell'oggetto come secondo. Nota che è probabile che sia lento, quindi potresti voler aggiungere del caching.

3

La mia soluzione, sulla base di informazioni provenienti da @JonSkeet e @CodeInChaos è di non guardare esclusivamente al PropertyInfo nell'espressione, ma anche il tipo di componente membro del MemberExpression:

/// <summary> 
/// Extracts the PropertyInfo for the propertybeing accessed in the given expression. 
/// </summary> 
/// <remarks> 
/// If possible, the actual owning type of the property is used, rather than the declaring class (so if "x" in "() => x.Foo" is a subclass overriding "Foo", then x's PropertyInfo for "Foo" is returned rather than the declaring base class's PropertyInfo for "Foo"). 
/// </remarks> 
/// <typeparam name="T"></typeparam> 
/// <param name="propertyExpression"></param> 
/// <returns></returns> 
internal static PropertyInfo ExtractPropertyInfo<T>(Expression<Func<T>> propertyExpression) 
{ 
    if (propertyExpression == null) 
    { 
     throw new ArgumentNullException("propertyExpression"); 
    } 

    var memberExpression = propertyExpression.Body as MemberExpression; 
    if (memberExpression == null) 
    { 
     throw new ArgumentException(string.Format("Expression not a MemberExpresssion: {0}", propertyExpression), "propertyExpression"); 
    } 

    var property = memberExpression.Member as PropertyInfo; 
    if (property == null) 
    { 
     throw new ArgumentException(string.Format("Expression not a Property: {0}", propertyExpression), "propertyExpression"); 
    } 

    var getMethod = property.GetGetMethod(true); 
    if (getMethod.IsStatic) 
    { 
     throw new ArgumentException(string.Format("Expression cannot be static: {0}", propertyExpression), "propertyExpression"); 
    } 

    Type realType = memberExpression.Expression.Type; 
    if(realType == null) throw new ArgumentException(string.Format("Expression has no DeclaringType: {0}", propertyExpression), "propertyExpression"); 

    return realType.GetProperty(property.Name); 
} 
Problemi correlati