Решение требует несколько немного расширенных функций 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;
}
}
шишка ........... – Xtro
Вы можете применить контракты на поля и (авто) свойства, а также. Это возможно с помощью PostSharp: '[Обязательная] строка f1;', '[Обязательный] public string P1 {get; задавать; } '. Проверка инициируется, когда значение присваивается полю или свойства. Решает ли он ваши потребности? Если нет, пожалуйста, не могли бы вы более подробно описать свой вариант использования? –
Я добавил свой случай использования – Xtro