我刚刚开始评估 PostSharp Ultimate,我想在程序集中强制实现一些自定义架构约束。
程序集的结构如下: (基本上每个命名空间都有一个根接口(interface)和该接口(interface)的特定实现)
MycompanyNamespace.Core.CommandDispatcher
ICommandDispatcher
XCommandDispatcher
YCommandDispatcher
...
MycompanyNamespace.Core.Services
IService
XService
YService
...
MycompanyNamespace.Core.Provider
IProvider
XProvider
YProvider
...
我要执行的规则:
- 不允许引用上游,例如不允许在声明了
IProvider
接口(interface)的命名空间中的类型引用IService
或ICommandDispatcher
类型的命名空间中的类型宣布 - 允许下游引用
我已经尝试了 PostSharp 附带的 ComponentInteral
约束,还创建了自定义 ReferentialConstraint
。
我不确定
使用正面规则还是负面规则更好?例如
[GrantAccess(TargetNamespace = typeof(IProvider), GrantedBy = typeof(ICommandDispatcher), typeof(IService)]
[ProhibitAccess(TargetNamespace = typeof(ICommandDispatcher), ProhibitedBy = typeof(IProvider), typeof(IService)]
我可以将规则放入 GlobalApsects.cs 文件中吗? assemlby 还是我需要用属性修饰类型?
我可以重复使用预装规则吗?或者有人会如何实现这样的自定义规则?
最佳答案
GrantAccess 或 ProhibitAccess
在 GrantAccess 和 ProhibitAccess 方法之间进行选择时,我不会说只有一个选项绝对优于另一个选项。 这取决于您的设计细节以及您想要执行约束的严格程度。
想到的问题是:
给定命名空间中的类对于该程序集是公共(public)类还是内部类?
如果将来添加新的命名空间/组件怎么办? 默认情况下,新命名空间中的类是否应该能够访问该命名空间?还是应该明确授予此访问权限?
假设示例中的所有类都是内部类,您希望在它们之间强制执行引用约束。 在这种情况下,GrantAccess 为您提供更严格的控制。新添加的命名空间将无法访问 现有命名空间,除非您审查设计并明确授予此访问权限。
或者您可能希望公开您的服务类,并限制它们在 Provider 命名空间中的使用。必须为每个使用服务的外部 namespace 显式添加 GrantAccess 是非常不方便的。在这种情况下,限制较少的 ProhibitAccess 方法可能更好。
这些只是您可以做出判断的示例,但最终取决于您的设计和项目。
程序集或类型级别的属性
由于您希望将约束应用于给定命名空间中的所有类,因此将属性应用于程序集(在 GlobalAspects.cs 中)并在 AttributeTargetTypes 属性中指定命名空间会更加方便。
// Grant access to all classes in MycompanyNamespace.Core.Services namespace
[assembly: GrantAccess(..., AttributeTargetTypes = "MycompanyNamespace.Core.Services.*", ...)]
自定义约束
PostSharp 提供的 ComponentInternal
属性实现了 GrantAccess 方法,因此您可以将其应用于该用例。
但是,似乎存在一个错误,不允许它在程序集级别上多次应用。这应该在即将发布的版本之一中得到修复。
要使用类似的逻辑实现您的自定义约束,您可以从以下示例开始:
[MulticastAttributeUsage(
MulticastTargets.AnyType | MulticastTargets.Method | MulticastTargets.InstanceConstructor | MulticastTargets.Field,
TargetTypeAttributes = MulticastAttributes.UserGenerated,
TargetMemberAttributes = (MulticastAttributes.AnyVisibility & ~MulticastAttributes.Private) | MulticastAttributes.UserGenerated)]
[AttributeUsage(
AttributeTargets.Assembly |
AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum | AttributeTargets.Delegate,
AllowMultiple = true)]
public class GrantAccessAttribute : ReferentialConstraint
{
private string[] namespaces;
public GrantAccessAttribute(params Type[] types)
{
this.namespaces = types.Select(t => t.Namespace).ToArray();
}
public override void ValidateCode(object target, Assembly assembly)
{
MemberInfo targetMember = target as MemberInfo;
if (targetMember != null)
{
Type targetType = target as Type;
if (targetType != null)
{
// validate derived types
foreach (TypeInheritanceCodeReference reference in ReflectionSearch.GetDerivedTypes(targetType, ReflectionSearchOptions.IncludeTypeElement))
{
Validate(reference);
}
// validate member references
foreach (MemberTypeCodeReference reference in ReflectionSearch.GetMembersOfType(targetType, ReflectionSearchOptions.IncludeTypeElement))
{
Validate(reference);
}
}
// validate references in methods
foreach (MethodUsageCodeReference methodUsageCodeReference in ReflectionSearch.GetMethodsUsingDeclaration(targetMember))
{
Validate(methodUsageCodeReference);
}
}
}
private void Validate(ICodeReference codeReference)
{
string usingNamespace = GetNamespace(codeReference.ReferencingDeclaration);
string usedNamespace = GetNamespace(codeReference.ReferencedDeclaration);
if (usingNamespace.Equals(usedNamespace, StringComparison.Ordinal))
return;
if (this.namespaces.Any(
x => usingNamespace.Equals(x, StringComparison.Ordinal) ||
(usingNamespace.StartsWith(x, StringComparison.Ordinal) && usingNamespace[x.Length] == '.')))
return;
object[] arguments = new object[] {/*...*/};
Message.Write(MessageLocation.Of(codeReference.ReferencingDeclaration), SeverityType.Warning, "ErrorCode", "Access error message.", arguments);
}
private string GetNamespace(object declarationObj)
{
Type declaringType = declarationObj as Type;
if (declaringType == null)
{
MemberInfo declaringMember;
ParameterInfo parameter;
LocationInfo location;
if ((declaringMember = declarationObj as MemberInfo) != null)
{
declaringType = declaringMember.DeclaringType;
}
else if ((location = declarationObj as LocationInfo) != null)
{
declaringType = location.DeclaringType;
}
else if ((parameter = declarationObj as ParameterInfo) != null)
{
declaringType = parameter.Member.DeclaringType;
}
else
{
throw new Exception("Invalid declaration object.");
}
}
return declaringType.Namespace;
}
}
关于c# - 使用 postsharp 的自定义类型引用规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22831894/