2017-01-09 10 views
1

Как проверить состояние объекта (пример: значение поля bool) в записи метода с помощью PostSharp?Как проверить состояние объекта в методе или записи свойства с помощью PostSharp?

Это также возможно для имущества или сеттера? Возможно также авто-недвижимость?

Я знаю, как проверять параметры метода с помощью пользовательских контрактов и как перехватывать метод через метод, но я не знаю, как передать правило проверки состояния из атрибута аспекта в тело ввода метода.

Мой случай использования:

Я хочу, чтобы все инициализируются проверки в method1, 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; 
    } 

Проблема заключается в том, что 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 перехватывает каждый метод обеспечивает SelectMethods способом с кодом в методе OnInvoke. Проблема в том, что вы не хотите перехватывать методы Init с проверкой «условие поля», иначе вызов этого метода вызовет исключение с «Awesome text», и было бы невозможно инициализировать класс. Поэтому вы должны пометить методы, которые невозможно перехватить.Вы можете либо использовать конвенции и дать такое же имя для всех 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; 
    } 
} 

EDIT: можно отметить «Cond ition поле»атрибут вместо указания его имени в виде строки:

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

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

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

Удивительный ответ. Я еще не тестировал его, но я буду отмечать его как правильный ответ. Я буду отчитываться, если у меня возникнут проблемы при его реализации. Спасибо!! – Xtro

+0

Кроме того, я могу использовать ключевое слово nameof вместо строки «Initialized». Это дает мне немного поддержки intellisense. – Xtro