IL编织器 --- Fody

这篇具有很好参考价值的文章主要介绍了IL编织器 --- Fody。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

介绍

这个项目的名称“Fody”来源于属于织巢鸟科(Ploceidae)的小鸟(Fody),本身意义为编织。

核心Fody引擎的代码库地址 :https://github.com/Fody/Fody

Github上是这样介绍的:

Fody 是一个用于织制 .NET 程序集的可扩展工具。它允许在构建过程中作为一部分来操纵程序集的中间语言(IL),这需要大量的底层代码编写。这些底层代码需要了解 MSBuildVisual StudioAPIFody 通过可扩展的插件模型试图消除这些底层代码。这种技术非常强大,例如,可以将简单属性转换为完整的 INotifyPropertyChanged 实现,添加对空参数的检查,添加方法计时,甚至使所有字符串比较都不区分大小写。

Fody 处理的底层任务包括:

  • MSBuild 任务注入到构建流程中。
  • 解析程序集和 pdb 文件的位置。
  • 抽象了与 MSBuild 日志记录的复杂性。
  • 将程序集和 pdb 文件读入 Mono.Cecil 对象模型中。
  • 根据需要重新应用强名称。
  • 保存程序集和 pdb 文件。

Fody 使用 Mono.Cecil 和基于插件的方法在编译时修改 .NET 程序集的中间语言(IL)。

  • 它不需要额外的安装步骤来构建。
  • 属性是可选的,具体取决于所使用的编织器。
  • 不需要部署运行时依赖项。

插件

从介绍就可以看出,理论上只要你想要,基于这个库基本上能做任何事情。

所以基于该库,诞生了非常非常多的插件库,下面简单介绍及编写Demo简单使用

插件 描述 Github URL
Fody 编织.net程序集的可扩展工具 https://github.com/Fody/Fody
AutoProperties.Fody 这个外接程序为您提供了对自动属性的扩展控制,比如直接访问backing字段或拦截getter和setter。 https://github.com/tom-englert/AutoProperties.Fody
PropertyChanged.Fody 将属性通知添加到实现INotifyPropertyChanged的所有类。 https://github.com/Fody/PropertyChanged
InlineIL.Fody 在编译时注入任意IL代码。 https://github.com/ltrzesniewski/InlineIL.Fody
MethodDecorator.Fody 通过IL重写编译时间装饰器模式 https://github.com/Fody/MethodDecorator
NullGuard.Fody 将空参数检查添加到程序集 https://github.com/Fody/NullGuard
ToString.Fody 给属性生成ToString()方法 https://github.com/Fody/ToString
Rougamo.Fody 在编译时生效的AOP组件,类似于PostSharp。 https://github.com/inversionhourglass/Rougamo

AutoProperties.Fody

这个插件提供了对自动属性的扩展控制,比如直接访问backing字段或拦截getter和setter。

using System;
using AutoProperties;
using Xunit;

public class AutoPropertiesInterceptor
{
    [Fact]
    public void Run()
    {
        Assert.Equal(10, Property1);
        Assert.Equal("11", Property2);

        Property1 = 42;

        Assert.Equal(45, Property1);
        Assert.Equal("11", Property2);

        Property2 = "44";

        Assert.Equal(45, Property1);
        Assert.Equal("47", Property2);
    }

    [GetInterceptor]
    T GetInterceptor<T>(string propertyName, T fieldValue)
    {
        return (T)Convert.ChangeType(Convert.ToInt32(fieldValue) + 1, typeof(T));
    }

    [SetInterceptor]
    void SetInterceptor<T>(T value, string propertyName, out T field)
    {
        field = (T)Convert.ChangeType(Convert.ToInt32(value) + 2, typeof(T));
    }

    public int Property1 { get; set; } = 7;

    public string Property2 { get; set; } = "8";
}

PropertyChanged.Fody

该插件在编译时将INotifyPropertyChanged代码注入属性中:

using System.ComponentModel;
using System.Runtime.CompilerServices;
using AutoProperties;
using Xunit;

public class AutoPropertiesSample : INotifyPropertyChanged
{
    int numberOfPropertyChangedCalls;

    public string AutoProperty1 { get; set; }
    public string AutoProperty2 { get; set; }

    public AutoPropertiesSample()
    {
        AutoProperty2.SetBackingField("42");
    }

    [Fact]
    public void Run()
    {
        // no property changed call was generated in constructor:
        Assert.Equal(0, numberOfPropertyChangedCalls);
        Assert.Equal("42", AutoProperty2);

        AutoProperty1 = "Test1";
        Assert.Equal(1, numberOfPropertyChangedCalls);
        Assert.Equal("Test1", AutoProperty1);

        AutoProperty1.SetBackingField("Test2");
        Assert.Equal(1, numberOfPropertyChangedCalls);
        Assert.Equal("Test2", AutoProperty1);
    }


    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        numberOfPropertyChangedCalls += 1;

        PropertyChanged?.Invoke(this, new(propertyName));
    }
}


除此之外,该插件附带了一个 C# 代码生成器,只需将实现 INotifyPropertyChanged 接口或包含 [AddINotifyPropertyChangedInterface] 属性的类标记为 partial,生成器将会自动添加必要的事件和事件触发器。

可以通过项目文件中的属性配置代码生成器:

<PropertyGroup>
  <PropertyChangedAnalyzerConfiguration>
    <IsCodeGeneratorDisabled>false</IsCodeGeneratorDisabled>
    <EventInvokerName>OnPropertyChanged</EventInvokerName>
  </PropertyChangedAnalyzerConfiguration>
</PropertyGroup>

更多用法建议查看官方文档。

InlineIL.Fody

该插件允许在编译时将任意IL注入到程序集中。

示例代码

using System;
using Xunit;
using static InlineIL.IL.Emit;
public class Sample
{
    [Fact]
    public void Run()
    {
        var item = new MyStruct
        {
            Int = 42,
            Guid = Guid.NewGuid()
        };

        ZeroInit.InitStruct(ref item);

        Assert.Equal(0, item.Int);
        Assert.Equal(Guid.Empty, item.Guid);
    }

    struct MyStruct
    {
        public int Int;
        public Guid Guid;
    }
}

public static class ZeroInit
{
    public static void InitStruct<T>(ref T value)
        where T : struct
    {
        Ldarg(nameof(value));

        Ldc_I4_0();

        Sizeof(typeof(T));

        Unaligned(1);

        Initblk();
    }
}

小技巧:这里可以借助ILDASM工具先生成想要的 IL 代码,在按照 IL 代码取编写要注入的 C# 代码,也可以参照我之前的文章工具 --- IL指令集解释,理解 IL 执行过程。

MethodDecorator.Fody

通过IL重写编译时装饰器模式。

定义拦截器属性:

using System;
using System.Reflection;
using MethodDecorator.Fody.Interfaces;

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Module)]
public class InterceptorAttribute : Attribute, IMethodDecorator
{
    public void Init(object instance, MethodBase method, object[] args)
    {
    }

    public void OnEntry()
    {
        InterceptionRecorder.OnEntryCalled = true;
    }

    public void OnExit()
    {
        InterceptionRecorder.OnExitCalled = true;
    }

    public void OnException(Exception exception)
    {
        InterceptionRecorder.OnExceptionCalled = true;
    }
}

定义拦截记录器

public static class InterceptionRecorder
{
    public static bool OnEntryCalled;
    public static bool OnExitCalled;
    public static bool OnExceptionCalled;

    public static void Clear()
    {
        OnExitCalled= OnEntryCalled = OnExceptionCalled = false;
    }
}

定义目标类

public static class Target
{
    [Interceptor]
    public static void MyMethod()
    {

    }

    [Interceptor]
    public static void MyExceptionMethod()
    {
        throw new("Foo");
    }
}

示例:

using Xunit;

public class MethodDecoratorSample
{
    [Fact]
    public void SimpleMethodSample()
    {
        InterceptionRecorder.Clear();
        Target.MyMethod();
        Assert.True(InterceptionRecorder.OnEntryCalled);
        Assert.True(InterceptionRecorder.OnExitCalled);
        Assert.False(InterceptionRecorder.OnExceptionCalled);
    }

    [Fact]
    public void ExceptionMethodSample()
    {
        InterceptionRecorder.Clear();
        try
        {
            Target.MyExceptionMethod();
        }
        catch
        {
        }
        Assert.True(InterceptionRecorder.OnEntryCalled);
        Assert.False(InterceptionRecorder.OnExitCalled);
        Assert.True(InterceptionRecorder.OnExceptionCalled);
    }
}

NullGuard.Fody

该插件向程序集添加null参数检查,支持三种操作模式:隐式模式显式模式可为空引用类型模式

  • 在隐式模式下,假定一切都不为空,除非标记为 [AllowNull]。这是 NullGuard 一直以来的工作方式。
  • 在显式模式下,假定一切都可为空,除非标记为 [NotNull]。这种模式旨在支持 ReSharper(R#)的可为空性分析,使用悲观模式。
  • 在可为空引用类型模式下,使用 C# 8 可为空引用类型(NRT)注释来确定类型是否可为空。

如果没有显式配置,NullGuard 将按以下方式自动检测模式:

  • 如果检测到 C# 8 可为空属性,则使用可为空引用类型模式。
  • 引用 JetBrains.Annotations 并在任何地方使用 [NotNull] 将切换到显式模式。
  • 如果不满足上述条件,则默认为隐式模式。

示例:

using Xunit;

public class NullGuardSample
{
    [Fact(Skip = "Explicit")]
    public void Run()
    {
        var targetClass = new TargetClass();
        Assert.Throws<ArgumentNullException>(() => targetClass.Method(null));
    }
}

public class TargetClass
{
    public void Method(string param)
    {
    }
}

ToString.Fody

该插件可以从带有[ToString]属性修饰的类的公共属性中生成ToString方法。

using System.Diagnostics;
using Xunit;

public class ToStringSample
{
    [Fact]
    public void Run()
    {
        var target = new Person
                     {
                         GivenNames = "John",
                         FamilyName = "Smith"

                     };
        Debug.WriteLine(target.ToString());
        Assert.Equal("{T: \"Person\", GivenNames: \"John\", FamilyName: \"Smith\"}", target.ToString());
    }
}

[ToString]
class Person
{
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    [IgnoreDuringToString]
    public string FullName => $"{GivenNames} {FamilyName}";
}

Rougamo.Fody

Rougamo是一个静态代码织入的AOP组件,类似Postsharp的一个组件,具有 MethodDecorator.Fody的功能,但功能更加强大,我个人觉得最为突出,优秀的两个功能点:

  • 匹配
  • 编织

匹配指的是命中AOP要拦截的目标匹配,比如有特征匹配,表达式匹配,类型匹配,更细化到模糊匹配,正则匹配。

编制则指的是拦截后能做的操作,比如有重写方法参数,修改返回值,异常处理,重试等。

该插件很强大,示例代码太多,就不再本篇内列出示例代码,官方文档中文介绍非常详细,建议直接查看官方文档。

其他

在Github库中,它提供了一些插件使用的Demo,除以上简单介绍的部分插件以外,还有这些

<Weavers VerifyAssembly="true"
         VerifyIgnoreCodes="0x80131869"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
 
  <Anotar.Catel />
  <Anotar.Splat />
  <Anotar.Serilog />
  <Anotar.NLog />
  <Anotar.Custom />
  <Anotar.CommonLogging />
  <AsyncErrorHandler />
  <BasicFodyAddin />
  <Caseless />
  <ConfigureAwait  ContinueOnCapturedContext="false" />
  <EmptyConstructor />
  <ExtraConstraints />
  <Equatable />
  <InfoOf />
  <Ionad />
  <Janitor />
  <MethodTimer />
  <ModuleInit />
  <Obsolete />
  <PropertyChanging />
  <PropertyChanged />
  <Validar />
  <Resourcer />
  <Publicize />
  <Virtuosity />
  <Visualize />
</Weavers>

若是在 Visual StudioNuGet 管理器中搜索 Fody 相关包,会有更多的一些三方或者小众的库,依旧值得尝试。

小结

Fody 实现原理上就能看出,这个库很强非常强。加上现在已有的非常之多的插件,除了能够提升开发效率之外,以在一定程度上实现一些难以实现的功能。强烈推荐大家学习使用。

链接

Fody官方Demo:https://github.com/Fody/FodyAddinSamples

工具 --- IL指令集解释:https://niuery.com/post/61文章来源地址https://www.toymoban.com/news/detail-711448.html

到了这里,关于IL编织器 --- Fody的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 10.本项目的简单介绍及所用工具

    本项目的层次架构适合初学者或者有一些基础的同学,项目整体简单明了,有着非常严谨的逻辑思维,并且在前面文章中也讲了一些项目中所需要的软件安装配置以及一些前置的在本项目中所需要的java基础知识。 股票量化交易项目 股票量化交易是一种利用计算机程序和数学

    2024年02月07日
    浏览(24)
  • ChatGPT的来源-InstructGPT论文简要介绍

    现在大火的ChatGPT功能十分强大,不仅可以回答用户问题,编写故事,甚至还可以写代码。ChatGPT跟OpenAI之前发表的InstructGPT使用的模型方法比较类似,只是训练的数据不同,为了探索ChatGPT的原理,笔者找来2022年3月发表的InstructGPT的论文,做了简要的介绍。 ChatGPT,美国OpenAI 研

    2024年02月02日
    浏览(33)
  • WIFI抗干扰分析介绍及来源

    目录 1.WLAN抗干扰分析 2.无线干扰的分类和来源 2.1.WLAN干扰 2.1.1. 同频干扰 2.1.2.邻频干扰 2.2.WLAN外部的干扰 3.无线干扰的检测 4.无线干扰的避免和消减 4.1.RRM 4.2.频谱分析 4.3.信道复用 5. 其他无线干扰避免和消减措施 6.结束语 今天, WLAN 已经不再仅仅是最初的一种简便的网络接

    2024年02月07日
    浏览(28)
  • 人工智能|各名称与概念之介绍

    版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com 适莽苍者,三餐而反,腹犹果然;适百里者,宿舂粮;适千里者,三月聚粮。 ——庄周《逍遥游》 上文引用了战国中期先秦道家学派的代表人物庄子的逍遥游选段,翻译为白话文大致含义是:到近郊去的

    2024年03月17日
    浏览(39)
  • 名称服务器(Name Server)介绍

       名称服务器 (Name Server)是互联网域名系统(DNS)中的一部分,它们负责将用户可读的域名(例如:www.example.com)解析为计算机可识别的IP地址(例如:192.168.1.1)。这使得我们可以使用容易记忆的域名访问网站,而不是需要记住复杂数字的IP地址。   名称服务器通常

    2024年01月24日
    浏览(28)
  • WPF插件之 - PropertyChanged.Fody插件的使用详解

    PropertyChanged.Fody 主要是实现了INotifyPropertyChanged 接口的,然后通过特性对外提供相关属性通知功能。 引用该插件能够使我们属性通知的代码更为简洁。 源码:https://github.com/Fody/PropertyChanged 如在阅读本文后,后续遇到文中没有提及的问题或知识点,可以查看源码说明文档 1.首

    2024年02月05日
    浏览(17)
  • C# 通过Costura.Fody把DLL合并到exe程序中

    打包独立的exe程序有多种方法,这里只说Costura.Fody。 我们用VS发布应用程序可以借助Costura.Fody直接打包成一个独立的exe程序,但是一些非托管的做了几次都没打进去,最后成功了,这里记录一下。 或者可以通过这里获取 https://github.com/Fody/Costura/tree/develop 我的版本是5.7.0安装好

    2024年02月06日
    浏览(37)
  • 这个标题用了最简单朴素的语言,而且把Qiskit作为工具进行介绍。

    作者:禅与计算机程序设计艺术 近年来,随着机器学习、大数据、云计算等技术的蓬勃发展,人工智能领域迎来了一个全新的变革。在这一带伤口已经积累到一定程度的当下,如何通过应用计算机科学的技术来解决这个复杂而又迷人的课题,成为了很多人梦寐以求的东西。比

    2024年02月07日
    浏览(24)
  • WinForm】使用Costura.Fody打包编译成可独立运行的桌面程序

    在建项目的时候要注意,选择 Windows 窗体应用(.NET Framework)或者wpf项目 ,然后打开 在解决方案资源管理器中,选择刚才的项目名,鼠标右键找到并打开 管理NuGet包 ,然后在 浏览 选项卡里,输入一个 Costura.Fody 并查找,有就点安装,安装前需要注意选择支持的对应 版本 和

    2024年03月19日
    浏览(34)
  • 前端面试:【DOM】编织网页的魔法

    嘿,亲爱的代码魔法师!在JavaScript的奇幻世界里,有一项强大的技能,那就是 DOM操作 。DOM(文档对象模型)操作允许你选择、修改和创建网页元素,就像是在编织一个魔法的网页。 1. 什么是DOM? DOM是一个表示网页结构的树状结构,它把网页的每个部分都表示为一个对象,

    2024年02月11日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包