[英]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的解決方案(免責聲明:我是男人),其中一些具有免費版本。
您可以使用PostSharp方面,並使用CompileTimeInitialize讀取屬性名稱。
例如:
[Serializable]
class MyCustomAttribute : LocationLevelAspect
{
string propertyName;
public override void CompileTimeInitialize( LocationInfo targetLocation,
AspectInfo aspectInfo )
{
this.propertyName = targetLocation.PropertyName;
}
}
免費的PostSharp社區版具有此功能。
問題是使用System.Reflection無法看到以這種方式構建的自定義屬性。
您還可以使用添加自定義屬性的方面。 該方面然后應實現IAspectProvider,並返回CustomAttributeIntroductionAspect的實例。 此頁面上有一個示例可以啟發您。 PostSharp專業版($)上提供此功能。
您還可以使自定義屬性類(任何類,而不是特定的方面)實現接口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.