2017-01-09 42 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

相关问题