PostSharp версии 6.7.12 для инструментов и библиотек. У меня есть несколько пользовательских атрибутов:
[PSerializable]
public sealed class CheckInvariantAttribute : OnMethodBoundaryAspect
{
// Snip - build-time logic here...
public override void OnExit(MethodExecutionArgs args)
{
// Check stuff...
}
}
[Serializable]
public sealed class NotDefaultAttribute : LocationContractAttribute,
ILocationValidationAspect<Guid>,
ILocationValidationAspect<DateTime>,
IValidableAnnotation
{
// Snip - build-time logic here...
public Exception ValidateValue(
DateTime value,
string locationName,
LocationKind locationKind,
LocationValidationContext contect)
{
// Snip, do stuff...
}
public Exception ValidateValue(
Guid value,
string locationName,
LocationKind locationKind,
LocationValidationContext contect)
{
// Snip, do stuff...
}
}
... и я также использую другой доступный атрибут контракта кода. Я применяю оба из них к одному и тому же методу:
[CheckInvariant]
public virtual void DoSomething(
[NotNull] SomeObjectType inst,
[NotDefault] DateTime someVal,
[StrictlyPositive] Decimal someAmt)
{
// Snip, do stuff...
}
Когда я это делаю, я получаю предупреждение во время компиляции, в котором говорится Conflicting aspects on "My.Namespace.MyClass.DoSomething( /* insert parameters here */ )": transformations "My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal' and "My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit" are not commutative, but they are not strongly ordered. Their order of execution is undeterministic.
Прежде всего, я ожидаю, что они должны быть детерминированными - один применяет аспект к параметру, а другой применяется при выходе из метода. Здесь естественный порядок. Кроме того, я пытался пометить их как коммуникативные, я пытался упорядочить их по роли или по приоритету, и во всех случаях потерпел неудачу. (Возможно, я сделал это неправильно.)
Как избавиться от этих предупреждений?
Вот результат сборки для этого примера местоположения:
1>C:\MyProject\SomeClass.cs(159,23,159,27): warning PS0114: Conflicting aspects on "My.Namespace.MyClass.DoSomething(SometObjectType, System.DateTime, System.Decimal)": transformations "My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal'" and "My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit" are not commutative, but they are not strongly ordered. Their order of execution is undeterministic.
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Detail of dependencies for the previous warnings:
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Marker BEFORE (54):
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: (no dependency)
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: PostSharp.Patterns.Contracts.NotNullAttribute: Validates the value passed to parameter 'inst':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.NotDefaultAttribute: Validates the value passed to parameter 'someVal':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Wrapped by advice(s) OnExit:
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Action=Order, Position=After, Condition = {equals "My.Namespace.CheckInvariantAttribute: Marker BEFORE (54)"}
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Action=Order, Position=Before, Condition = {equals "My.Namespace.CheckInvariantAttribute: Marker AFTER (54)"}
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: PostSharp.Patterns.Contracts.StrictlyPositiveAttribute: Validates the value passed to parameter 'someAmt':
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: Provide Role=Validation
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: My.Namespace.CheckInvariantAttribute: Marker AFTER (54):
1>Namespace\MyClass.cs(159,23,159,27): message PS0124: (no dependency)





Пока атрибут [NotDefault] применяется к параметру, фактическое преобразование применяется к телу метода. И это в основном то же преобразование OnMethodBoundary, что и с аспектом [CheckInvariant]. Вы можете представить себе преобразованное тело метода, имеющее следующую структуру:
NotDefaultOnEntry
try
{
CheckInvariantOnEntry
try
{
// original method body
CheckInvariantOnSuccess
}
catch
{
CheckInvariantOnException
}
finally
{
CheckInvariantOnExit
}
NotDefaultOnSuccess
}
catch
{
NotDefaultOnException
}
finally
{
NotDefaultOnExit
}
Как видите, порядок аспектов влияет на порядок, в котором будет применено обертывание тела метода. Конечно, в вашем конкретном случае аспекты не предоставляют всех возможных советов, и конечная структура тела метода проще. В этом случае PostSharp по-прежнему должен выдавать предупреждение, чтобы избежать неожиданностей и непредвиденного поведения.
Вы можете использовать зависимости роли аспекта, чтобы упорядочить эти аспекты. Примените [ProvideAspectRole] к NotDefaultAttribute и [AspectRoleDependency] к CheckInvariantAttribute, как показано ниже.
[Serializable]
[ProvideAspectRole(StandardRoles.Validation)]
public sealed class NotDefaultAttribute : LocationContractAttribute,
ILocationValidationAspect<Guid>,
ILocationValidationAspect<DateTime>,
IValidableAnnotation
{
// ...
}
[PSerializable]
[AspectRoleDependency(AspectDependencyAction.Order, AspectDependencyPosition.After, StandardRoles.Validation)]
public sealed class CheckInvariantAttribute : OnMethodBoundaryAspect
{
// ...
}