如何使用 PostSharp 验证方法或 属性 条目中的对象状态?
How to validate the object state in method or property entry using PostSharp?
如何使用 PostSharp 验证方法条目中的对象状态(示例:布尔字段的值)?
属性 getter 或 setter 也可以吗?
自动属性也可以吗?
我知道如何通过自定义契约验证方法参数以及如何通过 methodboundry 拦截方法,但我不知道如何将状态验证规则从 aspect 属性传递到方法入口主体。
我的用例:
我希望 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;
}
解决方案需要一些稍微高级的 PostSharp 功能。
您需要能够从您的方面访问 "condition field",并且您需要能够配置哪个字段是 "condition field"。不幸的是,C# 允许您将常量表达式指定为属性参数。唯一的方法(我知道)是使用字符串来指定 "condition field" 名称:
[Conditional( "Initialized", "Awesome text"]
void Method1()
{
//do stuff;
}
问题是智能感知不适用于字符串,但您可以在您的方面验证该字段的存在。
您可以使用 ImportLocationAdviceInstance and implementing IAdviceProvider 导入目标 class 的任何字段:
[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
要么包含绑定到 "condition field",要么 PostSharp 发出 ERR001 或 ERR002 如果给定名称的 "condition field" 不存在或声明为其他类型bool
.
下一步是拦截目标class方法验证码:
[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
方法提供的每个方法。问题是你不想用 "condition field" 检查拦截 Init
方法,否则调用这个方法会抛出 "Awesome text" 的异常并且不可能初始化一个class。所以你必须以某种方式标记无法拦截的方法。您可以使用约定并为所有 Init
方法指定相同的名称,或者您可以使用属性标记 Init
方法:
[AttributeUsage(AttributeTargets.Method)]
public class InitAttribute : Attribute
{
}
您可以使用 MethodPointcut 到 select public 没有 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;
}
}
编辑:可以通过属性标记 "condition field" 而不是将其名称指定为字符串:
[Conditional("Awesome text")]
class MyClass
{
[ConditionField]
bool Initialized;
[Init]
void Init()
{
Initialized = true;
}
void Method1()
{
//do stuff;
}
}
如何使用 PostSharp 验证方法条目中的对象状态(示例:布尔字段的值)?
属性 getter 或 setter 也可以吗? 自动属性也可以吗?
我知道如何通过自定义契约验证方法参数以及如何通过 methodboundry 拦截方法,但我不知道如何将状态验证规则从 aspect 属性传递到方法入口主体。
我的用例:
我希望 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;
}
解决方案需要一些稍微高级的 PostSharp 功能。
您需要能够从您的方面访问 "condition field",并且您需要能够配置哪个字段是 "condition field"。不幸的是,C# 允许您将常量表达式指定为属性参数。唯一的方法(我知道)是使用字符串来指定 "condition field" 名称:
[Conditional( "Initialized", "Awesome text"]
void Method1()
{
//do stuff;
}
问题是智能感知不适用于字符串,但您可以在您的方面验证该字段的存在。
您可以使用 ImportLocationAdviceInstance and implementing IAdviceProvider 导入目标 class 的任何字段:
[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
要么包含绑定到 "condition field",要么 PostSharp 发出 ERR001 或 ERR002 如果给定名称的 "condition field" 不存在或声明为其他类型bool
.
下一步是拦截目标class方法验证码:
[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
方法提供的每个方法。问题是你不想用 "condition field" 检查拦截 Init
方法,否则调用这个方法会抛出 "Awesome text" 的异常并且不可能初始化一个class。所以你必须以某种方式标记无法拦截的方法。您可以使用约定并为所有 Init
方法指定相同的名称,或者您可以使用属性标记 Init
方法:
[AttributeUsage(AttributeTargets.Method)]
public class InitAttribute : Attribute
{
}
您可以使用 MethodPointcut 到 select public 没有 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;
}
}
编辑:可以通过属性标记 "condition field" 而不是将其名称指定为字符串:
[Conditional("Awesome text")]
class MyClass
{
[ConditionField]
bool Initialized;
[Init]
void Init()
{
Initialized = true;
}
void Method1()
{
//do stuff;
}
}