2017-01-09 66 views
1

如何在使用PostSharp的方法條目中驗證對象狀態(例如:bool字段的值)?如何使用PostSharp驗證方法或屬性條目中的對象狀態?

對屬性獲取者或設置者來說,這也是可能的嗎? 也可能用於自動屬性?

我知道如何通過自定義合同驗證方法參數,以及如何通過methodboundry截獲方法,但我不知道如何將狀態驗證規則從aspect屬性傳遞給方法入口主體。

我的使用情況:

我想在方法1,2和3的所有初始化檢查一個方面來處理。

不方面:

class MyClass 
{ 
    bool Initialized; 

    void Init() 
    { 
     //do stuff; 
     Initialized = true; 
    } 

    void Method1() 
    { 
     if (!Initialized) throw AwesomeException("Awesome text"); 

     //do stuff; 
    } 

    void Method2() 
    { 
     if (!Initialized) throw AwesomeException("Awesome text"); 

     //do stuff; 
    } 

    void Method3() 
    { 
     if (!Initialized) throw AwesomeException("Awesome text"); 

     //do stuff; 
    } 
} 

縱橫:

[Conditional(<<somehow define condition field here>> Initialized, "Awesome text"] 
    void Method1() 
    { 
     //do stuff; 
    } 

    [Conditional(<<magic>> Initialized, "Awesome text"] 
    void Method2() 
    { 
     //do stuff; 
    } 

    [Conditional(<<magic>> Initialized, "Awesome text"] 
    void Method3() 
    { 
     //do stuff; 
    } 
+0

凹凸........... – Xtro

+0

您可以將合同領域和(自動)性能以及。這可以通過PostSharp實現:'[必需的]字符串f1;','[必需的] public string P1 {get;組; }'。將值分配給字段或屬性時,會觸發驗證。它是否能解決您的需求?如果沒有,請你能更詳細地描述你的用例嗎? –

+0

我添加了我的用例 – Xtro

回答

1

解決方案需要多個一點點先進PostSharp功能。

您需要能夠從您的方面訪問「條件字段」,並且您需要能夠配置哪個字段是「條件字段」。不幸的是C#允許你指定常量表達式作爲屬性參數。唯一的方法(我知道)使用字符串指定「條件字段」名稱:

[Conditional("Initialized", "Awesome text"] 
    void Method1() 
    { 
     //do stuff; 
    } 

的問題是,智能感知不能與字符串的工作,但你可以在你的方面驗證領域的存在。

您可以通過使用ImportLocationAdviceInstance和實施IAdviceProvider導入目標類的任何領域:

[PSerializable] 
public class ConditionalAttribute : InstanceLevelAspect, IAdviceProvider 
{ 
    private string conditionFieldName; 
    private string awesomeText; 
    public ILocationBinding ConditionBindingField; 

    public ConditionalAttribute(string conditionFieldName, string awesomeText) 
    { 
     this.conditionFieldName = conditionFieldName; 
     this.awesomeText = awesomeText; 
    } 

    public IEnumerable<AdviceInstance> ProvideAdvices(object targetElement) 
    { 
     var targetType = (Type) targetElement; 
     var bindingFieldInfo = this.GetType().GetField("ConditionBindingField", BindingFlags.Instance | BindingFlags.Public); 

     foreach (
      var field in 
      targetType.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | 
           BindingFlags.NonPublic)) 
     { 
      if (field.Name == conditionFieldName) 
      { 
       if (field.FieldType.IsAssignableFrom(typeof(bool))) 
       { 
        yield return new ImportLocationAdviceInstance(bindingFieldInfo, new LocationInfo(field)); 
        yield break; 
       } 
       { 
        Message.Write(MessageLocation.Of(targetType), SeverityType.Error, "ERR002", $"{targetType.Name} contains {field.FieldType.Name} {conditionFieldName}. {conditionFieldName} has to be bool."); 
        yield break; 
       } 
      } 
     } 

     Message.Write(MessageLocation.Of(targetType), SeverityType.Error, "ERR001", $"{targetType.Name} doesn't contain {conditionFieldName}"); 
    } 
} 

現在,ConditionBindingField或者包含綁定到「條件字段」,或PostSharp發出ERR001或ERR002如果「條件字段「的名稱不存在或與bool以外的其他類型聲明。

下一步是監聽目標類中的方法與驗證代碼:

[OnMethodInvokeAdvice] 
[MethodPointcut("SelectMethods")] 
public void OnInvoke(MethodInterceptionArgs args) 
{ 
    bool conditionFieldValue = (bool)ConditionBindingField.GetValue(args.Instance); 
    if (!conditionFieldValue) 
    { 
     throw new InvalidOperationException(awesomeText); 
    } 

    args.Proceed(); // call original method body 
} 

PostSharp截獲SelectMethods方法與OnInvoke方法中的代碼提供的每個方法。問題是你不想用「條件字段」檢查來攔截Init方法,否則對這個方法的調用會拋出一個帶有「真棒文本」的異常,並且不可能初始化一個類。所以你必須標記不能被攔截的方法。您可以使用慣例和把同一個名字給所有Init方法,或者你可以標記Init方法與屬性:

[AttributeUsage(AttributeTargets.Method)] 
public class InitAttribute : Attribute 
{ 
} 

您可以使用MethodPointcut選擇公共實例方法,而沒有Init屬性:

private IEnumerable<MethodInfo> SelectMethods(Type type) 
{ 
    const BindingFlags bindingFlags = BindingFlags.Instance | 
     BindingFlags.DeclaredOnly | BindingFlags.Public; 

    return type.GetMethods(bindingFlags) 
     .Where(m => !m.GetCustomAttributes(typeof(InitAttribute)).Any()); 
} 

有條件方面的使用示例:

[Conditional("Initialized", "Awesome text")] 
class MyClass 
{ 
    bool Initialized; 

    [Init] 
    void Init() 
    { 
     Initialized = true; 
    } 

    void Method1() 
    { 
     //do stuff; 
    } 
} 

編輯:有可能標記「cond銀行足球比賽場」由一個屬性,而不是名稱指定爲字符串:

[Conditional("Awesome text")] 
class MyClass 
{ 
    [ConditionField] 
    bool Initialized; 

    [Init] 
    void Init() 
    { 
     Initialized = true; 
    } 

    void Method1() 
    { 
     //do stuff; 
    } 
} 
+0

令人驚歎的答案。我還沒有測試它,但我會將其標記爲正確答案。如果在實施時遇到任何問題,我會報告。謝謝!! – Xtro

+0

此外,我可以使用nameof關鍵字而不是「初始化」字符串。這給了我一點intellisense的支持。 – Xtro

相關問題