c# - 将代码注入(inject)所有没有自定义属性的方法和属性的最简单方法

标签 c# .net aop code-injection

关于AOP的问答很多在 .NET在 Stack Overflow 上,经常提到 PostSharp 和其他第三方产品。因此,在 .NET 和 C# 世界中似乎有相当多的 AOP 选项。但是每一个都有其限制,并且在下载了有前途的 PostSharp 我在他们的文档中发现“方法必须是虚拟的”才能注入(inject)代码(编辑:参见 ChrisWue 的回答和我的评论 - 虚拟约束一定是在其中一个竞争者身上,我想)。我没有进一步调查此声明的准确性,但它的绝对性让我回到了 Stack Overflow。

所以我想得到这个非常具体的问题的答案:

我想注入(inject)简单“if (some-condition) Console.WriteLine”风格的代码到每个方法和属性(静态、密封、内部、虚拟、非-virtual,没关系)在我的项目中没有自定义注释,以便在运行时动态测试我的软件。此注入(inject)代码不应保留在发布版本中,它仅用于开发期间的动态测试(与线程相关)。

最简单的方法是什么?我偶然发现 Mono.Cecil ,这看起来很理想,只是您似乎必须编写要在 IL 中注入(inject)的代码。这不是一个大问题,使用 Mono.Cecil 很容易获得用 C# 编写的代码的 IL 版本。但是,如果有更简单的东西,甚至最好内置​​到 .NET 中(我仍在使用 .NET 3.5),我想知道。 [更新:如果建议的工具不是 .NET Framework 的一部分,如果它是开源的(如 Mono.Cecil)或免费提供就更好了]

最佳答案

我能够用 Mono.Cecil 解决问题。我仍然对它的易学、易用和强大感到惊讶。几乎完全缺乏文档并没有改变这一点。

这些是我使用的 3 个文档来源:

第一个链接提供了一个非常温和的介绍,但由于它描述了旧版本的 Cecil - 在此期间发生了很多变化 - 第二个链接对将介绍翻译为 Cecil 0.9 非常有帮助。开始后,(也没有记录)源代码非常宝贵,它回答了我遇到的每一个问题 - 可能是关于 .NET 平台的一般问题,但我敢肯定网上某处有大量书籍和资料。

我现在可以获取 DLL 或 EXE 文件,对其进行修改,然后将其写回磁盘。我还没有做的唯一一件事是弄清楚如何保持调试信息——文件名、行号等当前在编写 DLL 或 EXE 文件后丢失。我的背景不是 .NET,所以我在这里猜测,我的猜测是我需要查看 mono.cecil.pdb 来解决这个问题。稍后的某个地方 - 现在对我来说并不是那么重要。我正在创建这个 EXE 文件,运行该应用程序——它是一个复杂的 GUI 应用程序,经过多年的发展,带有你希望在这样的软件中找到的所有包袱——它会检查内容并记录错误我。

这是我的代码的要点:

DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
// so it won't complain about not finding assemblies sitting in the same directory as the dll/exe we are going to patch
assemblyResolver.AddSearchDirectory(assemblyDirectory);
var readerParameters = new ReaderParameters { AssemblyResolver = assemblyResolver };

AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyFilename, readerParameters);

foreach (var moduleDefinition in assembly.Modules)
{
    foreach (var type in ModuleDefinitionRocks.GetAllTypes(moduleDefinition))
    {
        foreach (var method in type.Methods)
        {
            if (!HasAttribute("MyCustomAttribute", method.method.CustomAttributes)
            {
              ILProcessor ilProcessor = method.Body.GetILProcessor();
              ilProcessor.InsertBefore(method.Body.Instructions.First(), ilProcessor.Create(OpCodes.Call, threadCheckerMethod));
// ...

private static bool HasAttribute(string attributeName, IEnumerable<CustomAttribute> customAttributes)
{
    return GetAttributeByName(attributeName, customAttributes) != null;
}

private static CustomAttribute GetAttributeByName(string attributeName, IEnumerable<CustomAttribute> customAttributes)
{
    foreach (var attribute in customAttributes)
        if (attribute.AttributeType.FullName == attributeName)
            return attribute;
    return null;
}

如果有人知道完成此操作的更简单方法,我仍然对答案感兴趣,我不会将此标记为解决方案 - 除非没有更简单的解决方案出现。

关于c# - 将代码注入(inject)所有没有自定义属性的方法和属性的最简单方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6672080/

相关文章:

c# - .NET CLR 运行时方法替换

c# - Azure 中的 ASP.NET MVC Core 应用程序

c# - 使用表达式创建带有嵌套类的谓词

java - 是否可以在第三方类上切入点以及如何切入?

java - 自定义注释的 Spring AOP 切入点不适用于内部静态类

java - 如何更改运行时中的方法注释值?

c# - 使用匿名方法的例子和缺点

c# - 如何在C#中访问JS变量

c# - 使用哪个ORM?

c# - 解释 Func<double[]> 中的二维数组