经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
IL编织器 --- Fody
来源:cnblogs  作者:NiueryDiary  时间:2023/10/20 8:45:15  对本文有异议

介绍

fodyIcon.png

这个项目的名称“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。

  1. using System;
  2. using AutoProperties;
  3. using Xunit;
  4. public class AutoPropertiesInterceptor
  5. {
  6. [Fact]
  7. public void Run()
  8. {
  9. Assert.Equal(10, Property1);
  10. Assert.Equal("11", Property2);
  11. Property1 = 42;
  12. Assert.Equal(45, Property1);
  13. Assert.Equal("11", Property2);
  14. Property2 = "44";
  15. Assert.Equal(45, Property1);
  16. Assert.Equal("47", Property2);
  17. }
  18. [GetInterceptor]
  19. T GetInterceptor<T>(string propertyName, T fieldValue)
  20. {
  21. return (T)Convert.ChangeType(Convert.ToInt32(fieldValue) + 1, typeof(T));
  22. }
  23. [SetInterceptor]
  24. void SetInterceptor<T>(T value, string propertyName, out T field)
  25. {
  26. field = (T)Convert.ChangeType(Convert.ToInt32(value) + 2, typeof(T));
  27. }
  28. public int Property1 { get; set; } = 7;
  29. public string Property2 { get; set; } = "8";
  30. }

PropertyChanged.Fody

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

  1. using System.ComponentModel;
  2. using System.Runtime.CompilerServices;
  3. using AutoProperties;
  4. using Xunit;
  5. public class AutoPropertiesSample : INotifyPropertyChanged
  6. {
  7. int numberOfPropertyChangedCalls;
  8. public string AutoProperty1 { get; set; }
  9. public string AutoProperty2 { get; set; }
  10. public AutoPropertiesSample()
  11. {
  12. AutoProperty2.SetBackingField("42");
  13. }
  14. [Fact]
  15. public void Run()
  16. {
  17. // no property changed call was generated in constructor:
  18. Assert.Equal(0, numberOfPropertyChangedCalls);
  19. Assert.Equal("42", AutoProperty2);
  20. AutoProperty1 = "Test1";
  21. Assert.Equal(1, numberOfPropertyChangedCalls);
  22. Assert.Equal("Test1", AutoProperty1);
  23. AutoProperty1.SetBackingField("Test2");
  24. Assert.Equal(1, numberOfPropertyChangedCalls);
  25. Assert.Equal("Test2", AutoProperty1);
  26. }
  27. public event PropertyChangedEventHandler PropertyChanged;
  28. protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  29. {
  30. numberOfPropertyChangedCalls += 1;
  31. PropertyChanged?.Invoke(this, new(propertyName));
  32. }
  33. }

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

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

  1. <PropertyGroup>
  2. <PropertyChangedAnalyzerConfiguration>
  3. <IsCodeGeneratorDisabled>false</IsCodeGeneratorDisabled>
  4. <EventInvokerName>OnPropertyChanged</EventInvokerName>
  5. </PropertyChangedAnalyzerConfiguration>
  6. </PropertyGroup>

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

InlineIL.Fody

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

image.png

示例代码

  1. using System;
  2. using Xunit;
  3. using static InlineIL.IL.Emit;
  4. public class Sample
  5. {
  6. [Fact]
  7. public void Run()
  8. {
  9. var item = new MyStruct
  10. {
  11. Int = 42,
  12. Guid = Guid.NewGuid()
  13. };
  14. ZeroInit.InitStruct(ref item);
  15. Assert.Equal(0, item.Int);
  16. Assert.Equal(Guid.Empty, item.Guid);
  17. }
  18. struct MyStruct
  19. {
  20. public int Int;
  21. public Guid Guid;
  22. }
  23. }
  24. public static class ZeroInit
  25. {
  26. public static void InitStruct<T>(ref T value)
  27. where T : struct
  28. {
  29. Ldarg(nameof(value));
  30. Ldc_I4_0();
  31. Sizeof(typeof(T));
  32. Unaligned(1);
  33. Initblk();
  34. }
  35. }

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

image.png

MethodDecorator.Fody

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

定义拦截器属性:

  1. using System;
  2. using System.Reflection;
  3. using MethodDecorator.Fody.Interfaces;
  4. [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Module)]
  5. public class InterceptorAttribute : Attribute, IMethodDecorator
  6. {
  7. public void Init(object instance, MethodBase method, object[] args)
  8. {
  9. }
  10. public void OnEntry()
  11. {
  12. InterceptionRecorder.OnEntryCalled = true;
  13. }
  14. public void OnExit()
  15. {
  16. InterceptionRecorder.OnExitCalled = true;
  17. }
  18. public void OnException(Exception exception)
  19. {
  20. InterceptionRecorder.OnExceptionCalled = true;
  21. }
  22. }

定义拦截记录器

  1. public static class InterceptionRecorder
  2. {
  3. public static bool OnEntryCalled;
  4. public static bool OnExitCalled;
  5. public static bool OnExceptionCalled;
  6. public static void Clear()
  7. {
  8. OnExitCalled= OnEntryCalled = OnExceptionCalled = false;
  9. }
  10. }

定义目标类

  1. public static class Target
  2. {
  3. [Interceptor]
  4. public static void MyMethod()
  5. {
  6. }
  7. [Interceptor]
  8. public static void MyExceptionMethod()
  9. {
  10. throw new("Foo");
  11. }
  12. }

示例:

  1. using Xunit;
  2. public class MethodDecoratorSample
  3. {
  4. [Fact]
  5. public void SimpleMethodSample()
  6. {
  7. InterceptionRecorder.Clear();
  8. Target.MyMethod();
  9. Assert.True(InterceptionRecorder.OnEntryCalled);
  10. Assert.True(InterceptionRecorder.OnExitCalled);
  11. Assert.False(InterceptionRecorder.OnExceptionCalled);
  12. }
  13. [Fact]
  14. public void ExceptionMethodSample()
  15. {
  16. InterceptionRecorder.Clear();
  17. try
  18. {
  19. Target.MyExceptionMethod();
  20. }
  21. catch
  22. {
  23. }
  24. Assert.True(InterceptionRecorder.OnEntryCalled);
  25. Assert.False(InterceptionRecorder.OnExitCalled);
  26. Assert.True(InterceptionRecorder.OnExceptionCalled);
  27. }
  28. }

NullGuard.Fody

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

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

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

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

示例:

  1. using Xunit;
  2. public class NullGuardSample
  3. {
  4. [Fact(Skip = "Explicit")]
  5. public void Run()
  6. {
  7. var targetClass = new TargetClass();
  8. Assert.Throws<ArgumentNullException>(() => targetClass.Method(null));
  9. }
  10. }
  11. public class TargetClass
  12. {
  13. public void Method(string param)
  14. {
  15. }
  16. }

ToString.Fody

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

  1. using System.Diagnostics;
  2. using Xunit;
  3. public class ToStringSample
  4. {
  5. [Fact]
  6. public void Run()
  7. {
  8. var target = new Person
  9. {
  10. GivenNames = "John",
  11. FamilyName = "Smith"
  12. };
  13. Debug.WriteLine(target.ToString());
  14. Assert.Equal("{T: \"Person\", GivenNames: \"John\", FamilyName: \"Smith\"}", target.ToString());
  15. }
  16. }
  17. [ToString]
  18. class Person
  19. {
  20. public string GivenNames { get; set; }
  21. public string FamilyName { get; set; }
  22. [IgnoreDuringToString]
  23. public string FullName => $"{GivenNames} {FamilyName}";
  24. }

Rougamo.Fody

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

  • 匹配
  • 编织

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

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

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

其他

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

  1. <Weavers VerifyAssembly="true"
  2. VerifyIgnoreCodes="0x80131869"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  5. <Anotar.Catel />
  6. <Anotar.Splat />
  7. <Anotar.Serilog />
  8. <Anotar.NLog />
  9. <Anotar.Custom />
  10. <Anotar.CommonLogging />
  11. <AsyncErrorHandler />
  12. <BasicFodyAddin />
  13. <Caseless />
  14. <ConfigureAwait ContinueOnCapturedContext="false" />
  15. <EmptyConstructor />
  16. <ExtraConstraints />
  17. <Equatable />
  18. <InfoOf />
  19. <Ionad />
  20. <Janitor />
  21. <MethodTimer />
  22. <ModuleInit />
  23. <Obsolete />
  24. <PropertyChanging />
  25. <PropertyChanged />
  26. <Validar />
  27. <Resourcer />
  28. <Publicize />
  29. <Virtuosity />
  30. <Visualize />
  31. </Weavers>

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

小结

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

链接

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

工具 --- IL指令集解释:https://niuery.com/post/61

原文链接:https://www.cnblogs.com/pandefu/p/17775991.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号