c# - 将所有基本调用包装在派生类型中
问题描述
所以我有几个实例我希望能够做到这一点,但基本上我希望能够将所有对超类的调用包装在派生类型中。现在我正在尝试将所有对基本方法的调用包装在一个 Impersonator 中,但我也可以看到它的其他用途。
一个例子是
public void CopyFile(string filePath, string destPath)
{
using(var I = new Impersonator("user", ".", "password"))
{
base.CopyFile(string filePath, string destPath);
}
}
另一个方便的用途可能是
public void CopyFile(string filePath, string destPath)
{
try
{
base.CopyFile(string filePath, string destPath);
} catch(Exception e)
{
Log(e.Message);
}
}
现在我想类似地包装所有基本调用。有没有一种方便的方法可以做到这一点,还是我必须手动包装所有这些?
我正在寻找类似“超类中的 foreach baseMethod 执行此操作”之类的东西
也许找到某种方法来捕获类的传入调用并将它们包装为一个动作?
public void ActionWrapper(Action action)
{
try
{
action.Invoke();
} catch(Exception e)
{
Log(e.Message);
}
}
但是我怎么能以这种方式接听班级的电话呢?
老实说,这只是为了使类更易于维护并减少代码膨胀。我对这些或任何其他方法持开放态度。
解决方案
首先,我想赞扬你以这种方式解构代码的直觉。将错误处理/日志记录和安全/身份等问题与业务逻辑分开可以为可维护性创造奇迹。
您所描述的被称为装饰或拦截。Mark Seemann 有一篇很好的博客文章,比较了日志记录上下文中的两种方法。
在不使用外部工具(如 DI 或 AOP 框架)的情况下,我认为ActionWrapper
您提出的方法是一个好的开始。我对其进行了修改以显示模拟而不是日志记录,因为我认为模拟是一个更有趣的用例:
public void ActionWrapper(Action action)
{
using(var I = new Impersonator("user", ".", "password"))
{
action.Invoke();
}
}
所以问题是:如何有效地应用这种方法?
假设您现有的课程是:
public class FileCopier
{
public void CopyFile(string filePath, string destPath)
{
// Do stuff
}
}
正如您所建议的,您可以创建一个派生类来添加模拟:
public class FileCopierWithImpersonation : FileCopier
{
public void CopyFile(string filePath, string destPath)
=> WithImpersonation(base.CopyFile(filePath, destPath));
public void WithImpersonation(Action action)
{
using(var I = new Impersonator("user", ".", "password"))
{
action.Invoke();
}
}
}
在这里,FileCopierWithImpersonation
作为装饰器FileCopier
,通过继承实现。该WithImpersonation
方法用作拦截器,可以在任何方法上应用模拟范围。
这应该足够好,但它会在实施中产生一些妥协。基类的方法都需要标记为virtual
. 子类的构造函数可能需要将参数传递给基类。独立于基类的逻辑对子类的逻辑进行单元测试是不可能的。
因此,您可能想要提取一个接口 ( IFileCopier
) 并使用组合而不是继承来应用装饰器:
public class FileCopierWithImpersonation : IFileCopier
{
private readonly IFileCopier _decoratee;
public FileCopierWithImpersonation(IFileCopier decoratee)
{
// If you don't want to inject the dependency, you could also instantiate
// it here: _decoratee = new FileCopier();
_decoratee = decoratee;
}
public void CopyFile(string filePath, string destPath)
=> WithImpersonation(_decoratee.CopyFile(filePath, destPath));
public void WithImpersonation(Action action)
{
using(var I = new Impersonator("user", ".", "password"))
{
action.Invoke();
}
}
}
如果您使用的是 Visual Studio 2019,则有一个“通过...实现接口”的重构选项,它将通过调用相同类型的依赖项的方法来自动实现接口。之后,只需简单的查找/替换即可添加拦截器。
您还可以查看代码生成工具,例如自动生成装饰器的T4 模板。但请注意,.NET Core 不支持 T4。在这一点上,它看起来是一项遗留技术。
推荐阅读
- python - Python 3.7 导入模块而不编辑 sys.path 或使用相对路径的问题
- php - 在 laravel 关系中设置外表的位置
- laravel - Laravel 雄辩的分类
- excel - 使用 Application.Run 评估作为字符串传递的 Worksheet 函数
- h2 - H2 & Ignite 查询性能
- ios - 无法将“NSAttributedString.Key”类型的值转换为预期的字典键类型“String”错误(swift4.2)
- airflow - 在 minikube 上使用 helm 图表时气流网络服务器未启动
- php - 服务器之间的慢查询
- python - 执行connection.commit()后,为什么其他cursor.execute()不能正常工作?
- slider - Ionic 4 vue - 获取幻灯片索引