Inkslab.Intercept 1.2.18

dotnet add package Inkslab.Intercept --version 1.2.18
                    
NuGet\Install-Package Inkslab.Intercept -Version 1.2.18
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Inkslab.Intercept" Version="1.2.18" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Inkslab.Intercept" Version="1.2.18" />
                    
Directory.Packages.props
<PackageReference Include="Inkslab.Intercept" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Inkslab.Intercept --version 1.2.18
                    
#r "nuget: Inkslab.Intercept, 1.2.18"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Inkslab.Intercept@1.2.18
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Inkslab.Intercept&version=1.2.18
                    
Install as a Cake Addin
#tool nuget:?package=Inkslab.Intercept&version=1.2.18
                    
Install as a Cake Tool

Inkslab

GitHub language codeSize GitHub issues


目录


概述

本仓库包含两个独立但配套的 NuGet 包:

职责 核心 API
Inkslab.Expressions 基于 System.Reflection.Emit 的动态类型生成器,提供类 Expression 树语法 ModuleEmitterClassEmitterMethodEmitterExpression
Inkslab.Intercept 基于方法返回值类型的 AOP 拦截框架,依赖 Inkslab.Expressions 在运行时生成代理类 InterceptAttributeUseIntercept()InterceptContext

设计思想Inkslab.Expressions 将底层 IL 指令封装为高可读的表达式 API,Inkslab.Intercept 则在其之上实现了零侵入的 AOP 能力。


NuGet 包

PM> Install-Package Inkslab.Expressions
PM> Install-Package Inkslab.Intercept
Package NuGet Downloads 说明
Inkslab.Expressions Inkslab.Expressions Nuget 动态类型生成核心库
Inkslab.Intercept Inkslab.Intercept Nuget 基于方法返回值类型的 AOP 框架

Inkslab.Expressions — 动态类型生成

核心概念

ModuleEmitter           ← 动态程序集/模块的根容器
  └── ClassEmitter      ← 定义一个动态类(继承 AbstractTypeEmitter)
        ├── FieldEmitter        ← 字段
        ├── PropertyEmitter     ← 属性
        ├── ConstructorEmitter  ← 构造函数
        └── MethodEmitter       ← 方法体(继承 BlockExpression)
              └── Expression.*  ← 表达式节点(赋值/条件/循环/调用...)

生命周期ModuleEmitterDefineType → 定义成员 → CreateType() → 使用运行时类型。


快速上手

// 1. 创建模块(每个模块对应一个动态程序集)
var module = new ModuleEmitter("MyDynamicAssembly");

// 2. 定义类
var classEmitter = module.DefineType(
    "MyNamespace.Hello",
    TypeAttributes.Public | TypeAttributes.Class);

// 3. 定义字段
var nameField = classEmitter.DefineField("_name", typeof(string), FieldAttributes.Private);

// 4. 定义构造函数(接受 string 参数并赋值给字段)
var ctor = classEmitter.DefineConstructor(MethodAttributes.Public);
var nameParam = ctor.DefineParameter(typeof(string), ParameterAttributes.None, "name");
ctor.Append(Expression.Assign(nameField, nameParam));

// 5. 定义方法(直接返回字段值)
var greetMethod = classEmitter.DefineMethod("Greet", MethodAttributes.Public, typeof(string));
greetMethod.Append(nameField);

// 6. 编译并实例化
Type helloType = classEmitter.CreateType();
object instance = Activator.CreateInstance(helloType, "World");
string result = (string)helloType.GetMethod("Greet").Invoke(instance, null);
// result == "World"

ModuleEmitter — 程序集模块

ModuleEmitter 是所有动态类型的容器,对应一个内存中的动态程序集。

// 默认程序集名(Inkslab.Override)
var module = new ModuleEmitter();

// 自定义程序集名
var module = new ModuleEmitter("MyAssembly");

// 自定义名称 + 自定义文件名(.NET Framework 可保存到磁盘)
var module = new ModuleEmitter("MyAssembly", "MyAssembly.dll");

// 保存物理文件(仅 NET461+)
var module = new ModuleEmitter(savePhysicalAssembly: true);
方法 说明
DefineType(name, attributes) 定义公共或私有类,返回 ClassEmitter
DefineType(name, attributes, baseType) 定义继承自 baseType 的类
DefineType(name, attributes, baseType, interfaces[]) 定义实现多接口的类
DefineEnum(name, attributes, underlyingType) 定义枚举类型,返回 EnumEmitter
SaveAssembly() 保存程序集到磁盘(仅 NET461+)

常量ModuleEmitter.DEFAULT_ASSEMBLY_NAME = "Inkslab.Override"DEFAULT_FILE_NAME = "Inkslab.Override.dll"


ClassEmitter — 动态类

ClassEmitter 继承自 AbstractTypeEmitter,用于定义一个完整的动态 Class。

// 无基类
var cls = module.DefineType("Foo.Bar", TypeAttributes.Public | TypeAttributes.Class);

// 继承基类
var cls = module.DefineType("Foo.Bar", TypeAttributes.Public | TypeAttributes.Class, typeof(MyBase));

// 继承基类 + 实现接口
var cls = module.DefineType("Foo.Bar",
    TypeAttributes.Public | TypeAttributes.Class,
    typeof(MyBase),
    new[] { typeof(IDisposable), typeof(ICloneable) });

// 完成定义后编译
Type runtimeType = cls.CreateType();

AbstractTypeEmitter 定义成员的方法

方法 返回值 说明
DefineField(name, type, attributes) FieldEmitter 定义字段
DefineProperty(name, attributes, type) PropertyEmitter 定义属性
DefineMethod(name, attributes, returnType) MethodEmitter 定义方法
DefineConstructor(attributes) ConstructorEmitter 定义构造函数
DefineNestedType(name, attributes) ClassEmitter 定义嵌套类
DefineTypeInitializer() TypeInitializerEmitter 定义静态构造函数
OverrideMethod(methodInfo) MethodEmitter 重写/实现继承或接口方法

MethodEmitter — 方法体

MethodEmitter 继承自 BlockExpression,支持在方法体内 Append 任意表达式节点。

var method = classEmitter.DefineMethod(
    "Add",
    MethodAttributes.Public | MethodAttributes.Static,
    typeof(int));

var a = method.DefineParameter(typeof(int), ParameterAttributes.None, "a");
var b = method.DefineParameter(typeof(int), ParameterAttributes.None, "b");

// 方法体:return a + b;
method.Append(Expression.Add(a, b));
方法 说明
DefineParameter(type, attributes, name) 定义方法参数,返回 ParameterEmitter(可作为表达式使用)
Append(expression) 向方法体追加一个表达式节点
MakeGenericMethod(typeArguments[]) 获取泛型方法的实例化版本
SetCustomAttribute(builder) 添加自定义特性

ConstructorEmitter — 构造函数

var ctor = classEmitter.DefineConstructor(MethodAttributes.Public);
var param = ctor.DefineParameter(typeof(string), ParameterAttributes.None, "value");

// 调用基类构造函数(base(value))
ctor.Append(Expression.Base(classEmitter, baseCtor, param));

// 为字段赋值
ctor.Append(Expression.Assign(fieldEmitter, param));

FieldEmitter / PropertyEmitter — 字段与属性

// 字段
var field = classEmitter.DefineField("_count", typeof(int), FieldAttributes.Private);

// 属性(含 getter/setter)
var backingField = classEmitter.DefineField("_name", typeof(string), FieldAttributes.Private);

var getter = classEmitter.DefineMethod("get_Name",
    MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
    typeof(string));
getter.Append(backingField); // return _name;

var setter = classEmitter.DefineMethod("set_Name",
    MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
    typeof(void));
var valueParam = setter.DefineParameter(typeof(string), ParameterAttributes.None, "value");
setter.Append(Expression.Assign(backingField, valueParam)); // _name = value;

var prop = classEmitter.DefineProperty("Name", PropertyAttributes.None, typeof(string));
prop.SetGetMethod(getter);
prop.SetSetMethod(setter);

FieldEmitter 本身实现了 Expression,可直接当表达式节点使用(读取字段值)。PropertyEmitter 同理,可参与赋值与读取表达式。


EnumEmitter — 枚举

var enumEmitter = module.DefineEnum("MyNamespace.Color", TypeAttributes.Public, typeof(int));
enumEmitter.DefineLiteral("Red",   0);
enumEmitter.DefineLiteral("Green", 1);
enumEmitter.DefineLiteral("Blue",  2);

Type colorType = enumEmitter.CreateType();

Expression — 表达式系统

Expression 是所有表达式节点的抽象基类,所有节点只能通过静态工厂方法创建,不可直接 new。方法体(MethodEmitter)和构造函数体(ConstructorEmitter)本身继承自 BlockExpression,调用 Append(expression) 逐条追加语句,最后一条表达式的值即为方法返回值。

表达式覆盖了 C# 中常见的语句形式:

类别 代表方法
值与变量 ConstantDefaultVariableAssignThis
控制流 IfThenIfThenElseLoopForEachSwitchBreakContinueReturn
类型操作 ConvertTypeAsTypeIsCoalesce
方法与构造 CallDeclaringCallNewNewArrayArrayIndexMemberInitInvoke
运算符 AddSubtractMultiplyDivideModuloEqualAndAlsoOrElseNotAnd / Or / ExclusiveOrLeftShift / RightShiftPower 等(含 *Assign 复合赋值变体)
异常处理 ThrowTry / Try(finally) + .Catch(...) 链式
// 示例:生成 "return a + b"
var method = classEmitter.DefineMethod("Add",
    MethodAttributes.Public | MethodAttributes.Static, typeof(int));
var a = method.DefineParameter(typeof(int), ParameterAttributes.None, "a");
var b = method.DefineParameter(typeof(int), ParameterAttributes.None, "b");
method.Append(Expression.Add(a, b)); // Append 最后一条表达式即为返回值

几条容易被忽略的"贴近 C# 语义"约定

  • Equal / NotEqual 自带回退链:算术 ceq → enum vs underlying → 数值二元提升 → 用户 operator == → 可空提升(含异底层)→ IEquatable<T>.Equals(T) → 重写的 Equals(object) → 左右均为引用类型且具有继承/实现关系时退化为 object.ReferenceEquals;引用类型相等不需要手写 Call(Equals, ...)
  • Nullable<T> 上的相等比较与一元运算(Negate / Not / Increment / Decrement 等)自动按 lifted 语义生成 IL;底层不一致的 int? == longbyte? == short? 等也会先按 C# 数值提升表统一到公共底层再做可空比较。
  • C# 二元数值提升(算术 / 比较 / 位运算)按 ECMA spec 自动应用:int + long → longbyte == intshort & longuint < long → longulong + 有符号 拒绝。复合赋值(long += intint += bytedouble += floatlong &= int 等)仅当右侧能隐式转换到左侧类型时支持,否则保留 C# 编译错误同步的拒绝行为。
  • Try / Try(finally) 配合 .Catch(...).Catch(Type).Catch(VariableExpression) 链式构建 catch 块,没有独立的 TryCatch / TryFinally 工厂方法。

完整的工厂方法签名、节点继承关系、行为约束及二次封装注意事项,请参阅 Inkslab.Expressions.md(同时也是 Inkslab.Expressions NuGet 包的 README,按"签名 + 约束 + 示例"三段格式编排,对 AI 编程助手友好)。其中循环相关能力包含 Expression.Loop() 与统一的 Expression.ForEach(...),并对可空类型与引用类型相等比较做了自动语义对齐。


Inkslab.Intercept — AOP 拦截框架

Inkslab.Intercept 在运行时为 DI 容器中的服务类型自动生成代理子类,通过方法标记的 Attribute 实现无侵入拦截。

拦截器 Attribute 体系

根据被拦截方法的返回值类型选择对应的基类:

基类 适用返回值类型 需重写的方法
InterceptAttribute void void Run(InterceptContext, Intercept)
InterceptAsyncAttribute Task / ValueTask / Task<T> / ValueTask<T> Task RunAsync(InterceptContext, InterceptAsync)
ReturnValueInterceptAttribute T / Task<T> / ValueTask<T> T Run<T>(InterceptContext, Intercept<T>)
ReturnValueInterceptAsyncAttribute Task<T> / ValueTask<T> Task<T> RunAsync<T>(InterceptContext, InterceptAsync<T>)

继承关系InterceptAsyncAttribute 继承 ReturnValueInterceptAsyncAttributeReturnValueInterceptAttribute 继承 ReturnValueInterceptAsyncAttribute

注意T 特指 Task/ValueTask/Task<>/ValueTask<> 的具体业务类型。若方法有返回值但标记的是 InterceptAttributevoid 拦截器),则该拦截器不会生效

// 示例:void 方法拦截器(记录日志)
public class LogInterceptAttribute : InterceptAttribute
{
    public override void Run(InterceptContext context, Intercept intercept)
    {
        Console.WriteLine($"[Before] {context.Main.Name}");
        intercept.Run(context); // 调用原始方法
        Console.WriteLine($"[After] {context.Main.Name}");
    }
}

// 示例:有返回值拦截器(结果缓存)
public class CacheInterceptAttribute : ReturnValueInterceptAttribute
{
    public override T Run<T>(InterceptContext context, Intercept<T> intercept)
    {
        var result = intercept.Run(context);
        // ... 缓存 result
        return result;
    }
}

// 示例:异步拦截器(耗时统计)
public class TimingInterceptAsyncAttribute : InterceptAsyncAttribute
{
    public override async Task RunAsync(InterceptContext context, InterceptAsync intercept)
    {
        var sw = Stopwatch.StartNew();
        await intercept.RunAsync(context);
        Console.WriteLine($"{context.Main.Name} took {sw.ElapsedMilliseconds}ms");
    }
}

InterceptContext — 拦截上下文

拦截器方法执行时收到的上下文对象:

属性 类型 说明
Services IServiceProvider 当前请求的 DI 容器,可用于解析服务
Main MethodInfo 被拦截的原始方法
Inputs object[] 方法调用时传入的参数数组
public override void Run(InterceptContext context, Intercept intercept)
{
    var logger = context.Services.GetRequiredService<ILogger<MyService>>();
    logger.LogInformation("调用方法: {Method}, 参数: {Args}",
        context.Main.Name,
        string.Join(", ", context.Inputs));

    intercept.Run(context);
}

Intercept / InterceptAsync — 调用执行器

拦截器方法的第二个参数是"执行器",调用它的 Run / RunAsync 即执行原始方法(或调用链中下一个拦截器):

执行器类 方法 说明
Intercept void Run(InterceptContext) 执行 void 方法
Intercept<T> T Run(InterceptContext) 执行有返回值方法
InterceptAsync Task RunAsync(InterceptContext) 执行异步无返回值方法
InterceptAsync<T> Task<T> RunAsync(InterceptContext) 执行异步有返回值方法

可通过继承执行器类并重写 Run/RunAsync 来定制执行行为(例如修改参数或替换返回值)。


注册与激活

步骤一:在接口或类的方法上标记拦截器 Attribute:

public interface IOrderService
{
    [LogIntercept]
    void CreateOrder(OrderDto dto);

    [CacheIntercept]
    Task<Order> GetOrderAsync(int id);
}

步骤二:注册服务后调用 UseIntercept()

public void ConfigureServices(IServiceCollection services)
{
    // 先注册业务服务
    services.AddScoped<IOrderService, OrderServiceImpl>();

    // 激活拦截器(必须在所有需要拦截的服务注册之后调用)
    services.UseIntercept();
}

UseIntercept() 会扫描此时已注册的所有 ServiceDescriptor,为服务类型上存在拦截 Attribute 的条目自动生成代理类并替换原始注册。UseIntercept() 之后注册的服务不会被拦截


多拦截器链式执行

同一方法可标记多个拦截器 Attribute,框架通过 MiddlewareIntercept标记顺序依次执行(类似 ASP.NET Core 中间件管道):

[LogIntercept]      // 第 1 个执行
[TimingIntercept]   // 第 2 个执行
[CacheIntercept]    // 第 3 个执行(最内层,紧邻原始方法)
public virtual T GetData(int id) { ... }

执行顺序(洋葱模型):

LogIntercept.Before
  → TimingIntercept.Before
    → CacheIntercept.Before
      → 原始方法
    → CacheIntercept.After
  → TimingIntercept.After
→ LogIntercept.After

NoninterceptAttribute — 跳过拦截

在方法、属性、类或接口上标记 [Nonintercept],可以阻止该目标被拦截:

[Nonintercept] // 整个类不拦截
public class InternalService : IInternalService { ... }

public interface IMyService
{
    [Nonintercept] // 该方法不拦截
    void BypassMethod();

    [LogIntercept]
    void InterceptedMethod();
}

完整使用示例

// 1. 定义拦截器
public class LogInterceptAttribute : InterceptAttribute
{
    public override void Run(InterceptContext context, Intercept intercept)
    {
        Console.WriteLine($">> {context.Main.Name}({string.Join(", ", context.Inputs)})");
        intercept.Run(context);
        Console.WriteLine($"<< {context.Main.Name}");
    }
}

public class RetryInterceptAsyncAttribute : InterceptAsyncAttribute
{
    public override async Task RunAsync(InterceptContext context, InterceptAsync intercept)
    {
        for (int i = 0; i < 3; i++)
        {
            try
            {
                await intercept.RunAsync(context);
            }
            catch when (i < 2)
            {
                await Task.Delay(100);
            }
        }
    }
}

// 2. 在接口上标记
public interface IPaymentService
{
    [LogIntercept]
    void Pay(decimal amount);

    [RetryInterceptAsync]
    [LogIntercept]
    Task<bool> PayAsync(decimal amount);
}

// 3. 实现类(virtual 方法可被拦截;非 virtual 方法无法被拦截)
public class PaymentService : IPaymentService
{
    public virtual void Pay(decimal amount) { /* ... */ }
    public virtual async Task<bool> PayAsync(decimal amount) { /* ... */ }
}

// 4. 注册服务
services.AddScoped<IPaymentService, PaymentService>();
services.UseIntercept(); // 激活拦截器
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
.NET Framework net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.18 92 5/29/2026
1.2.17 100 5/13/2026
1.2.12 111 4/28/2026
1.2.10 108 4/24/2026
1.2.8 307 11/14/2025
1.2.7 186 10/10/2025
1.2.6 1,280 7/11/2024
1.2.5 841 7/5/2024
1.2.4 236 7/5/2024 1.2.4 is deprecated because it has critical bugs.
1.2.3 225 7/5/2024 1.2.3 is deprecated because it has critical bugs.
1.2.2 268 7/4/2024 1.2.2 is deprecated because it has critical bugs.
1.2.1 264 7/4/2024 1.2.1 is deprecated because it has critical bugs.
1.2.0.2 5,054 10/30/2023
1.2.0.1 223 10/30/2023
1.2.0 220 10/30/2023
1.1.0 243 10/21/2023
1.0.2.1 246 10/21/2023