解决方案需要多个一点点先进PostSharp功能。
您需要能够从您的方面访问“条件字段”,并且您需要能够配置哪个字段是“条件字段”。不幸的是C#允许你指定常量表达式作为属性参数。唯一的方法(我知道)使用字符串指定“条件字段”名称:
[Conditional("Initialized", "Awesome text"]
void Method1()
{
//do stuff;
}
的问题是,智能感知不能与字符串的工作,但你可以在你的方面验证领域的存在。
您可以通过使用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
方法,否则对这个方法的调用会抛出一个带有“真棒文本”的异常,并且不可能初始化一个类。所以你必须标记不能被拦截的方法。您可以使用惯例和把同一个名字给所有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;
}
}
编辑:有可能标记“cond银行足球比赛场”由一个属性,而不是名称指定为字符串:
[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