Вызвать функцию неявно после вызова функции - PullRequest
0 голосов
/ 31 октября 2018

Возможно ли реализовать такое поведение, как оно используется в модульных тестах? где вы можете анотировать с

[TestInitialize]

для выполнения этой функции каждый раз перед

[TestMethod]

выполнено. И так же

[TestCleanup]

всегда выполняется после TestMethod.

Ответы [ 3 ]

0 голосов
/ 31 октября 2018

Вы не можете сделать это из коробки. Вам нужно написать собственный код выполнения, который использует отражение, чтобы выяснить, что и как выполнять. Ниже я поделюсь простым примером.

Во-первых, вам нужны атрибуты для описания типа методов.

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

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

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

Мы будем использовать это на примере класса. Обратите внимание, что все методы в этом примере являются частными. Они могут быть вызваны через отражение. Также обратите внимание, что для простоты они не имеют параметров и ничего не возвращают. Вы можете обойти этот пример и изменить его на поддержку параметров.

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...");
   }
}

Следующий класс, называемый executor, берет объект и смотрит на его тип, идентифицируя методы и просматривая их атрибуты. В конце концов, если атрибут 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...
0 голосов
/ 31 октября 2018

Есть несколько альтернативных решений.

Например, вы можете использовать Методы шаблона , где инициализация и очистка определены в базовом классе, а ваш реализующий класс реализует только метод DoWorkImpl. Вызов DoWork вызовет Initialize, затем запустит метод 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.
    }
}

Другая альтернатива - использовать Действия .
Это будет выглядеть примерно так в (псевдо) коде:

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

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

private void Cleanup()
{
   // Cleanup code here.
}
0 голосов
/ 31 октября 2018

То, что вы ищете, называется Аспектно-ориентированное программирование и, к сожалению (или нет, зависит от того, кого вы спрашиваете) C # не реализует какой-либо нативный механизм для его поддержки. Реализация механизма AOP, безусловно, возможна, но не тривиальна, вам нужно использовать какой-то динамический прокси (связанные вопросы: .NET Core: атрибуты, которые выполняются до и после метода , как указано CodeConstruct и Как сделать простой динамический прокси в C # , также вот хорошая статья на эту тему ).

...