2012-02-27 34 views
8

我有一個父/子類層次結構中的父抽象聲明一個字符串屬性和子類實現它:如何讓孩子從表達式中聲明類型?

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

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

當我使用顯式(或隱式)使用兒童類的表情,我期待表達的的MemberInfo的DeclaringType是「孩子」,而是它是父:

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

因爲DeclaringType是家長斷言失敗。

有什麼我可以做聲明我的表達或消耗它來揭示兒童類型的實際使用?

注:GetMemberInfo()上面的擴展方法:

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

類'Child'不從'Parent'繼承! – vulkanino 2012-02-27 14:25:11

+1

什麼是GetMemberInfo()方法?如果是擴展名,請發佈其實施。 – Ani 2012-02-27 14:27:38

+1

「如果從中獲取此MemberInfo對象的Type對象未聲明此成員,則DeclaringType屬性將表示其基本類型之一。」 – vulkanino 2012-02-27 14:29:23

回答

8

否 - 這是什麼得到由C#編譯器發出準確的表示(我甚至忘了我們這樣寫的!)。當尋找成員時,覆蓋被忽略 - 編譯器只關心最初聲明爲成員的類型。您可以通過編譯代碼然後查看IL來看到這一點。這種方法:

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

被編譯成這個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 

一個瑣事點:VB編譯相同的方式工作,所以這種方法:

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

編譯爲:

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

哇,謝謝你的詳細回覆!我想這意味着:「這是不可能的」,所以我需要回到製圖板。我的實際目標是GetCustomAttributes(),但是一些自定義屬性被添加到子類中的重寫屬性,但使用上述我無法聯繫他們,因爲DeclaringType是Parent。 – Trinition 2012-02-27 14:30:58

+0

@Trinition:但是你可以得到成員引用的* target *的類型,即'child'的類型。這不夠好嗎?由於您的示例代碼無效,因此很難確切知道顯示的內容 - 「表達式」上沒有這種「GetMemberInfo」方法。 – 2012-02-27 14:34:06

+0

我編輯並添加了上面的GetMemberInfo()的主體。 – Trinition 2012-02-27 14:40:09

1

如果你不想使用靜態類型的方法,而是使用最新的重寫,那麼這是可能的。我沒有測試,但是類似於以下的東西應該做的工作:

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

你在哪裏傳遞MemberInfo作爲第一個參數,以及對象作爲第二的運行時類型。請注意,這可能很慢,因此您可能需要添加一些緩存。

3

我的解決方案的基礎上,從@JonSkeet和@CodeInChaos信息是不是在表達的純粹的PropertyInfo看,也是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); 
}