2017-01-09 3 views
1

PostSharp를 사용하는 메소드 항목에서 객체 상태 (예 : bool 필드의 값)를 검증하려면 어떻게해야합니까?PostSharp를 사용하여 메서드 또는 속성 항목의 개체 상태를 확인하는 방법은 무엇입니까?

는 속성 게터 또는 세터하는 것도 가능하다? 자동 속성을 사용할 수 있습니까?

나는 사용자 지정 계약을 통해 메소드 매개 변수의 유효성을 검사하는 방법 methodboundry를 통해 방법을 차단하는 방법하지만 방법 항목 본체 측면의 속성에서 상태 유효성 검사 규칙을 통과하는 방법을 몰라하는 방법을 알고있다.

내 사용 사례 :

나는 방법 항목, 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에서 가능합니다 :'[Required] string f1;','[필수] public string P1 {get; 세트; }'. 필드 또는 속성에 값이 할당되면 유효성 검사가 트리거됩니다. 그것은 당신의 필요를 해결합니까? 그렇지 않은 경우 사용 사례를 자세히 설명해 주시겠습니까? –

+0

유스 케이스를 추가했습니다. – Xtro

답변

1

솔루션은 여러 조금 고급 PostSharp 기능을 필요로합니다.

당신은 당신의 측면에서 "조건 필드"에 접근 할 수 있어야합니다 그리고 당신은 "조건 필드"인 필드 구성 할 수 있어야합니다. 불행히도 C#에서는 상수 표현식을 속성 인수로 지정할 수 있습니다. 유일한 방법은 (내가 알고)은 "조건 필드"를 지정하는 문자열을 사용 이름 :

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

문제는 IntelliSense를 문자열 작동하지 않지만 당신의 측면에서 필드의 존재를 확인할 수있다 .

당신은 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 인터셉트 OnInvoke 방법에서 코드 SelectMethods 방법에 의해 제공되는 각각의 방법

다음 단계는 인증 코드와 차단 대상 클래스 방법이다. 문제는 "조건 필드"검사를 사용하여 Init 메서드를 가로 채지 않으려는 것입니다. 그렇지 않으면이 메서드를 호출하면 예외가 발생하고 클래스를 초기화 할 수 없습니다. 따라서 요격 할 수없는 방법을 어떻게 든 표시해야합니다.당신은 사용 규칙과 하나가 모든 Init 방법에 같은 이름을 부여 할 수 있습니다 또는 당신은 속성 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()); 
} 
:

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

당신은 Init 특성이없는 공공 인스턴스 메소드를 선택 MethodPointcut을 사용할 수 있습니다

조건부 사용법의 예 :

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

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

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

편집 : "cond 대신 문자열로 이름을 지정하는 속성에 의해 ition 필드 "

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

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

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

놀라운 대답입니다. 아직 테스트하지 않았지만 정답으로 표시하겠습니다. 구현할 때 문제가 있으면 다시보고하겠습니다. 고맙습니다!! – Xtro

+0

또한 "초기화 된"문자열 대신 nameof 키워드를 사용할 수 있습니다. 그것은 약간의 인텔리 센스 지원을 제공합니다. – Xtro