使用Postsharp多次执行方法

时间:2019-05-08 10:08:44

标签: c# postsharp

我有一个具有n参数的方法。我想用保存这些n参数值的Attribute装饰这种方法。我受到限制,我不能仅提供诸如function(param1, param2)之类的参数来调用此方法,但是我可以让该函数具有默认参数,并且无需更改参数function()即可调用该函数,并具有要执行的属性设置了这些参数的方法:

[TestCase]
[Parameters(new object[] { 3, 0 })]
[Parameters(new object[] { 1, 1 })]
[Parameters(new object[] { 4, 4 })]
public void TestParameterized(double x = 0, double y = 0)
{
    Assert.AreEqual(x, y);
}

由于我的参数并不总是两倍,因此我转移了一个对象Array并进行了相应的转换。

[Serializable, AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class TestCaseAttribute : OnMethodBoundaryAspect
{
    public TestCaseAttribute()
    {
    }

    public override void OnEntry(MethodExecutionArgs args)
    {
        foreach (object attribute in args.Method.GetCustomAttributes(false))
        {
            if (attribute.GetType() == typeof(ParametersAttribute))
            {
                for (int i = 0; i < args.Arguments.Count; i++)
                {
                    args.Arguments.SetArgument(i, Convert.ToDouble(((ParametersAttribute)attribute).Params[i]));
                }

                base.OnEntry(args);
            }
        }

    }
}

(“参数属性”仅包含给定的参数)

在这里,我遍历我拥有的所有有效参数,并将它们强制转换(以进行测试)加倍。然后,我想“调用”该方法,就像提供属性一样。

就像写的一样,该方法只执行一次。

有人可以帮助我解决这个问题吗?谢谢!

2 个答案:

答案 0 :(得分:1)

OnMethodBoundaryAspect仅修饰方法的执行。您需要一个MethodInterceptionAspect来拦截执行。见下文。

我已经使用CompileTimeInitialize方法卸载了部分逻辑以进行构建。

我敢将ParametersAttribute重命名为ArgumentsAttribute

我还必须添加一个额外的参数以避免递归。

class Program
{
    static void Main(string[] args)
    {
        new Program().TestParameterized();
    }

    [TestCase]
    [Arguments(new object[] { 3, 0 })]
    [Arguments(new object[] { 1, 1 })]
    [Arguments(new object[] { 4, 4 })]
    public void TestParameterized(double x = 0, double y = 0, bool recursive = false)
    {
        Console.WriteLine($"{x} == {y}: {x == y}");
    }
}

[Serializable, AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class TestCaseAttribute : MethodInterceptionAspect
{
    private object[][] argumentsCollection;

    public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
    {
        var argumentAttributes = method.GetCustomAttributes(typeof(ArgumentsAttribute)).ToArray();
        argumentsCollection = new object[argumentAttributes.Length][];

        for (int i = 0; i < argumentAttributes.Length; i++)
        {
            object[] givenArguments = ((ArgumentsAttribute)argumentAttributes[i]).Arguments;
            object[] arguments = new object[givenArguments.Length + 1];
            Array.Copy(givenArguments, arguments, givenArguments.Length);
            arguments[givenArguments.Length] = true;
            argumentsCollection[i] = arguments;
        }
    }

    public override void OnInvoke(MethodInterceptionArgs args)
    {
        if ((bool)args.Arguments[args.Arguments.Count - 1])
        {
            args.Proceed();
            return;
        }

        foreach (var arguments in argumentsCollection)
        {
            args.Method.Invoke(args.Instance, arguments);
        }
    }
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
class ArgumentsAttribute : Attribute
{
    public object[] Arguments { get; }

    public ArgumentsAttribute(object[] arguments)
    {
        Arguments = arguments;
    }
}

答案 1 :(得分:0)

您为什么不使用goto的老式方式?

类似这样的东西:

foreach (object attribute in args.Method.GetCustomAttributes(false))
{
restart:
   if (attribute.GetType() == typeof(ParametersAttribute))
   {
       for (int i = 0; i < args.Arguments.Count; i++)
       {
          args.Arguments.SetArgument(i, Convert.ToDouble(((ParametersAttribute)attribute).Params[i]));
       }

   base.OnEntry(args);
   if(somecondition)
   {
       goto restart;
   }
   }
}

只需将restartgoto restart;放在需要重新启动代码的位置即可。