簡體   English   中英

如何將編譯器檢查的屬性名稱/表達式樹傳遞給自定義屬性

[英]How to pass compiler checked property names / Expression tree to a custom attribute

在某些地方,我注意到表達式樹作為參數傳遞給方法,以允許編譯器檢查屬性名。 例如,Caliburn Micro在其PropertyChangedBase類中具有以下方法簽名:

public virtual void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property);

我有一個自定義屬性,希望對構造器中的屬性名稱進行相同類型的編譯器檢查,以便輸入:

[MyCustomAttribute(() => PropertyName)]

代替:

[MyCustomAttribute("PropertyName")]

按照以下方式使用構造函數定義:

public MyCustomAttribute(params Expression<Func<object>>[] properties)

但是,由於對屬性參數的限制是常量表達式,因此這似乎是不可能的。

誰能推薦一種不同的方法,讓編譯器檢查屬性參數中的屬性名稱,而不是將這種潛在的錯誤留在僅使用字符串的地方?

編輯:感謝馬克的回答,我現在已經實現了這一點:

#if DEBUG
            foreach (var propertyInfo in
                GetType().GetProperties().Where(propertyInfo => Attribute.IsDefined(propertyInfo, typeof (MyCustomAttribute))))
            {
                foreach (var propertyName in propertyInfo.GetAttributes<MyCustomAttribute>(true)
                    .SelectMany(attribute => attribute.PropertyNames))
                    Debug.Assert(
                        GetType().GetProperty(propertyName) != null,
                        "Property not found",
                        "Property {0} declared on attributed property {1} was not found on type {2}.",
                        propertyName, propertyInfo.Name, GetType().Name
                    );
            }
#endif

這根本不可能。 屬性僅限於非常基本的類型,其中不包括您需要的類型。 做到這一點的一種可能的靜態安全方法是為每個屬性子類化屬性但這是一件瘋狂的工作。

就個人而言,我只是編寫一個單元測試,以查找所有出現的屬性並通過反射檢查它們是否有意義。 您也可以在#if DEBUG塊(或類似代碼)內的主代碼中執行此操作。

有幾種使用PostSharp的解決方案(免責聲明:我是男人),其中一些具有免費版本。

解決方案1

您可以使用PostSharp方面,並使用CompileTimeInitialize讀取屬性名稱。

例如:

[Serializable]
class MyCustomAttribute : LocationLevelAspect
{
  string propertyName;

  public override void  CompileTimeInitialize( LocationInfo targetLocation,
                                               AspectInfo aspectInfo )
  {
      this.propertyName = targetLocation.PropertyName;
  }
}  

免費的PostSharp社區版具有此功能。

問題是使用System.Reflection無法看到以這種方式構建的自定義屬性。

解決方案2

您還可以使用添加自定義屬性的方面。 該方面然后應實現IAspectProvider,並返回CustomAttributeIntroductionAspect的實例。 此頁面上有一個示例可以啟發您。 PostSharp專業版($)上提供此功能。

解決方案3

您還可以使自定義屬性類(任何類,而不是特定的方面)實現接口IValidableAnnotation:

public class MyAttribute : Attribute, IValidableAnnotation
{
    private string propertyName;

    public MyAttribute(string propertyName)
    {
       this.propertyName = propertyName;
    }

    public bool CompileTimeValidate( object target )
    {
       PropertyInfo targetProperty = (PropertyInfo) target;
       if ( targetProperty.Name != propertyName )
       {
          Message.Write( Severity.Error, "MY001", 
             "The custom attribute argument does not match the property name.");
          return false;
       }
    }
}

使用免費版本的PostSharp可以做到這一點,並且您可以輕松地將其包含在#if /#endif塊中,以使您的代碼完全獨立於PostSharp。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM