调用函数后隐式调用函数

Call a function implicitly after a function is called

是否可以像在单元测试中那样实现这种行为 你可以在哪里注释

[TestInitialize]

每次在

之前执行这个函数
[TestMethod]

被执行。同样的方法

[TestCleanup]

总是在 TestMethod 之后执行。

您要找的是 Aspect Oriented Programming and unfortunately (or not, depends on who you ask) C# doesn't implement any native mechanism to support it. Implementing an AOP engine is certainly possible but not trivial, you'll need to use some kind of dynamic proxy (related questions: as pointed by CodeConstruct and How to make a simple dynamic proxy in C#; also here's a nice article about the subject).

您不能开箱即用。您需要编写自己的执行代码,使用反射来确定执行什么以及如何执行。下面,我将分享一个简单的例子。

首先,您需要描述方法类型的属性。

class InitMethodAttribute : Attribute
{
   public InitMethodAttribute():base()
   { }
};

class CleanupMethodAttribute : Attribute
{
   public CleanupMethodAttribute() : base()
   { }
};

class RunMethodAttribute : Attribute
{
   public RunMethodAttribute() : base()
   { }
};

我们将在示例中使用它 class。请注意,此示例中的所有方法都是私有的。它们可以通过反射调用。另外,请注意,为简单起见,它们没有参数,也没有 return 任何内容。您可以解决此示例并将其更改为也支持参数。

class Example
{
   [InitMethod]
   private void Init()
   {
      Console.WriteLine("Initializing...");
   }

   [InitMethod]
   private void InitMore()
   {
      Console.WriteLine("More initializing...");
   }

   [RunMethod]
   private void Run()
   {
      Console.WriteLine("Running...");
   }

   [CleanupMethod]
   private void Cleanup()
   {
      Console.WriteLine("Cleaning up...");
   }
}

下一个 class,称为执行器,获取一个对象并查看其类型,识别方法并查看它们的属性。最终,如果在任何方法上找到 RunMethod 属性,则执行该方法。在执行所有 InitMethod 装饰方法之前,然后执行所有 CleanupMethod 装饰方法。

   static class Executor
   {
      public static void Run(object obj)
      {
         var type = obj.GetType();
         var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

         var initMethods = new List<MethodInfo>();
         var cleanMethods = new List<MethodInfo>();
         foreach (var method in methods)
         {
            var initattrs = method.GetCustomAttributes(typeof(InitMethodAttribute));
            var cleanattrs = method.GetCustomAttributes(typeof(CleanupMethodAttribute));

            if (initattrs != null && initattrs.Count() > 0)
               initMethods.Add(method);
            else if (cleanattrs != null && cleanattrs.Count() > 0)
               cleanMethods.Add(method);
         }

         foreach (var method in methods)
         {
            var runattrs = method.GetCustomAttributes(typeof(RunMethodAttribute));
            if(runattrs != null)
            {
               var runattr = runattrs.FirstOrDefault();
               if(runattr != null)
               {
                  foreach (var m in initMethods)
                     m.Invoke(obj, null);

                  method.Invoke(obj, null);

                  foreach (var m in cleanMethods)
                     m.Invoke(obj, null);
               }
            }
         }
      }
   }

以下程序使用了所有这些:

class Program
{
   static void Main(string[] args)
   {
      var example = new Example();
      Executor.Run(example);
   }
}

输出为:

Initializing...
More initializing...
Running...
Cleaning up...

有几个替代解决方案。

首先,您可以使用 Template Methods,其中初始化和清理在基 class 中定义,而您的实现 class 仅实现 DoWorkImpl 方法。调用 DoWork 将调用 Initialize,然后 运行 在继承 class 中实现的 DoWorkImpl 方法,并以 Cleanup 结束。这在(伪)代码中看起来像这样:

public abstract class BaseClass
{
    public void DoWork()
    {
        Initialize();
        DoWorkImpl();
        CleanUp();
    }

    public abstract void DoWorkImpl();

    private void Initialize()
    {
       // Initialization code here.
    }

    private void Cleanup()
    {
       // Cleanup code here.
    }
}

另一种选择是使用 Actions
这在(伪)代码中看起来像这样:

public void SurroundWithInitializeAndCleanup(Action actionToWrap)
{
    Initialize();
    actionToWrap();
    Cleanup();
}

private void Initialize()
{
    // Initialization code here.
}

private void Cleanup()
{
   // Cleanup code here.
}