调用函数后隐式调用函数
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.
}
是否可以像在单元测试中那样实现这种行为 你可以在哪里注释
[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:
您不能开箱即用。您需要编写自己的执行代码,使用反射来确定执行什么以及如何执行。下面,我将分享一个简单的例子。
首先,您需要描述方法类型的属性。
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.
}