Inkslab.Expressions
1.2.12
dotnet add package Inkslab.Expressions --version 1.2.12
NuGet\Install-Package Inkslab.Expressions -Version 1.2.12
<PackageReference Include="Inkslab.Expressions" Version="1.2.12" />
<PackageVersion Include="Inkslab.Expressions" Version="1.2.12" />
<PackageReference Include="Inkslab.Expressions" />
paket add Inkslab.Expressions --version 1.2.12
#r "nuget: Inkslab.Expressions, 1.2.12"
#:package Inkslab.Expressions@1.2.12
#addin nuget:?package=Inkslab.Expressions&version=1.2.12
#tool nuget:?package=Inkslab.Expressions&version=1.2.12
Inkslab.Expressions
基于
System.Reflection.Emit的动态类型生成器:用类似System.Linq.Expressions的工厂式 API 直接编写 IL,无需手写 OpCode。本文件同时作为 NuGet 包的 README,并被设计为 AI 编程助手可直接消费的规格文档:所有 API 均给出签名 + 约束 + 示例三段式描述,章节锚点稳定不变。
安装
PM> Install-Package Inkslab.Expressions
| 目标框架 | 状态 |
|---|---|
net461 / net462 / net47x / net48 |
✅ 支持,可保存物理程序集 |
netstandard2.1 |
✅ 支持 |
net6.0 |
✅ 支持 |
AI Reading Notes(机器优先速查)
本节为给 AI 编程助手的硬性规则。生成代码前必须读完。
全局规则
- 一切节点必须通过
Expression静态工厂创建,禁止new XxxExpression(...)。 - 方法体即
BlockExpression:MethodEmitter/ConstructorEmitter继承自BlockExpression,调用.Append(expr)顺序追加语句。 - 块复用是只读的:
BlockExpression一旦被Append进父块即变只读,再追加抛AstException。复用前先构建完整再嵌入。 - 变量不可跨方法体:同一
VariableExpression实例只能用于一个MethodEmitter,跨方法体会导致LocalBuilder冲突。 CanWrite == true才能做赋值左值:仅VariableExpression、可写ParameterExpression、非readonlyFieldExpression、有 setter 的PropertyExpression、ArrayIndexExpression满足。- 空
void块自动 Pop:当BlockExpression.IsVoid == true且追加非 void 表达式时,框架自动插入Nop弹栈,无需手动平衡。 - 生命周期:
new ModuleEmitter(...)→module.DefineType(...)→ 定义成员 →cls.CreateType()→ 反射使用运行时Type。 - 泛型方法必须先
MakeGenericMethod:含IsGenericParameter的MethodInfo直接传入Expression.Call会抛异常。
命名空间
using Inkslab; // ModuleEmitter、Expression、ClassEmitter ... 全部在此
错误模式速查
| 错误现象 | 根因 |
|---|---|
AstException: 块已只读 |
BlockExpression 已经被 Append 进其它块 |
LocalBuilder 跨方法异常 |
VariableExpression 在两个 MethodEmitter 中重复使用 |
ArgumentException 类型不匹配 |
赋值/三目/Coalesce 的左右两侧类型不满足兼容规则 |
Break/Continue 越界 |
BreakExpression / ContinueExpression 没有出现在某个循环块(LoopExpression / ForEachExpression)内部 |
Switch 分支编译失败 |
Case(ConstantExpression) 与 Case(VariableExpression) 在同一个 Switch 中混用 |
5 行入门
var module = new ModuleEmitter("Demo");
var cls = module.DefineType("Demo.Hello", TypeAttributes.Public | TypeAttributes.Class);
var add = cls.DefineMethod("Add", MethodAttributes.Public | MethodAttributes.Static, typeof(int));
var a = add.DefineParameter(typeof(int), ParameterAttributes.None, "a");
var b = add.DefineParameter(typeof(int), ParameterAttributes.None, "b");
add.Append(Expression.Add(a, b)); // 方法体:return a + b;
Type t = cls.CreateType();
架构总览
ModuleEmitter // 动态程序集(每个模块 = 一个 AssemblyBuilder)
└── ClassEmitter // 一个动态类型,继承 AbstractTypeEmitter
├── FieldEmitter // 字段(本身是 Expression,可读可写)
├── PropertyEmitter // 属性(getter/setter 通过 MethodEmitter)
├── ConstructorEmitter // 构造函数体(继承 BlockExpression)
└── MethodEmitter // 方法体(继承 BlockExpression)
└── Expression.* // 节点工厂:赋值/分支/循环/调用/...
生命周期约束:
| 阶段 | 允许的操作 |
|---|---|
DefineType 后、CreateType 前 |
定义字段、属性、方法、构造函数、嵌套类型;向方法体 Append 表达式 |
CreateType 之后 |
反射使用,禁止再修改任何 emitter |
ModuleEmitter — 动态程序集
构造签名
ModuleEmitter()
ModuleEmitter(string assemblyName)
ModuleEmitter(string assemblyName, string moduleFileName)
ModuleEmitter(bool savePhysicalAssembly) // NET Framework 才有效
默认值常量:
| 常量 | 值 |
|---|---|
ModuleEmitter.DEFAULT_ASSEMBLY_NAME |
"Inkslab.Override" |
ModuleEmitter.DEFAULT_FILE_NAME |
"Inkslab.Override.dll" |
主要方法
| 方法 | 返回 | 说明 |
|---|---|---|
DefineType(name, attributes) |
ClassEmitter |
顶级类 |
DefineType(name, attributes, baseType) |
ClassEmitter |
指定基类 |
DefineType(name, attributes, baseType, interfaces[]) |
ClassEmitter |
基类 + 多接口 |
DefineEnum(name, attributes, underlyingType) |
EnumEmitter |
枚举类型 |
SaveAssembly() |
void |
仅 NET Framework,保存到磁盘 |
ClassEmitter / AbstractTypeEmitter — 动态类型
ClassEmitter与EnumEmitter、NestedClassEmitter共同继承自AbstractTypeEmitter。下表方法都属于基类。
| 方法 | 返回 | 说明 |
|---|---|---|
DefineField(name, type, attributes) |
FieldEmitter |
字段 |
DefineProperty(name, attributes, type) |
PropertyEmitter |
属性,需后续 SetGetMethod / SetSetMethod |
DefineMethod(name, attributes, returnType) |
MethodEmitter |
普通方法 |
DefineConstructor(attributes) |
ConstructorEmitter |
实例构造函数 |
DefineTypeInitializer() |
TypeInitializerEmitter |
静态构造函数 |
DefineNestedType(name, attributes) |
ClassEmitter |
嵌套类型 |
OverrideMethod(MethodInfo) |
MethodEmitter |
重写基类虚方法或实现接口方法 |
CreateType() |
Type |
结束定义,返回运行时类型 |
示例
var cls = module.DefineType(
"Demo.Foo",
TypeAttributes.Public | TypeAttributes.Class,
typeof(MyBase),
new[] { typeof(IDisposable) });
var field = cls.DefineField("_x", typeof(int), FieldAttributes.Private);
var ctor = cls.DefineConstructor(MethodAttributes.Public);
ctor.Append(Expression.Assign(field, Expression.Constant(0)));
Type runtime = cls.CreateType();
MethodEmitter — 方法体
继承自 BlockExpression,本身就是一个方法体级的代码块。
| 方法 | 说明 |
|---|---|
DefineParameter(type, attributes, name) |
定义参数,返回 ParameterEmitter(继承 ParameterExpression,可直接作为表达式使用) |
Append(Expression) |
追加一条语句;返回自身以支持链式 |
MakeGenericMethod(Type[]) |
获取该方法的泛型实例化版本 |
SetCustomAttribute(CustomAttributeBuilder) |
添加特性 |
GetParameters() |
获取所有已定义的 ParameterEmitter |
返回值规则
- 方法签名为
void:可不写Return,方法体执行完即返回。 - 方法签名有返回值:方法体需以一个返回该类型的表达式收尾,或显式
Expression.Return(value)。 - 框架在 Emit 阶段自动绑定方法出口标签,所有
Return都是br到该出口。
var add = cls.DefineMethod("Add",
MethodAttributes.Public | MethodAttributes.Static, typeof(int));
var a = add.DefineParameter(typeof(int), ParameterAttributes.None, "a");
var b = add.DefineParameter(typeof(int), ParameterAttributes.None, "b");
add.Append(Expression.Add(a, b)); // 末尾表达式即返回值
ConstructorEmitter — 构造函数
继承自 BlockExpression。框架默认在体首自动调用基类无参构造,如需调用自定义基类构造,使用 Expression.Base(...)。
var ctor = cls.DefineConstructor(MethodAttributes.Public);
var p = ctor.DefineParameter(typeof(string), ParameterAttributes.None, "value");
ctor.Append(Expression.Base(cls, baseCtorInfo, p)); // base(value)
ctor.Append(Expression.Assign(field, p)); // _field = value;
FieldEmitter / PropertyEmitter
FieldEmitter 与 PropertyEmitter 本身实现了 Expression:
- 直接
Append(field)表示读取字段值。 Expression.Assign(field, value)表示写入字段。- 属性访问通过
Expression.Property(...)或直接使用PropertyEmitter。
var backing = cls.DefineField("_name", typeof(string), FieldAttributes.Private);
var getter = cls.DefineMethod("get_Name",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
typeof(string));
getter.Append(backing); // return _name;
var setter = cls.DefineMethod("set_Name",
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
typeof(void));
var v = setter.DefineParameter(typeof(string), ParameterAttributes.None, "value");
setter.Append(Expression.Assign(backing, v)); // _name = value;
var prop = cls.DefineProperty("Name", PropertyAttributes.None, typeof(string));
prop.SetGetMethod(getter);
prop.SetSetMethod(setter);
EnumEmitter — 枚举
var e = module.DefineEnum("Demo.Color", TypeAttributes.Public, typeof(int));
e.DefineLiteral("Red", 0);
e.DefineLiteral("Green", 1);
e.DefineLiteral("Blue", 2);
Type colorType = e.CreateType();
Expression — 表达式系统
所有节点继承自抽象基类 Expression(命名空间 Inkslab)。
基类核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
RuntimeType |
Type |
运行时返回类型 |
IsVoid |
bool |
RuntimeType == typeof(void) |
IsContext |
bool |
是否为 this 上下文 |
CanWrite |
bool |
能否作为赋值左值(默认 false) |
CanWrite == true 的节点:VariableExpression、非 in 的 ParameterExpression、非 readonly FieldExpression、有 setter 的 PropertyExpression、ArrayIndexExpression。
节点继承图
Expression // 抽象基类
├── ConstantExpression // 常量
├── DefaultExpression // default(T)
├── ThisExpression // this
├── VariableExpression // 局部变量(CanWrite=true)
│ └── ParameterExpression // 方法参数
├── MemberExpression // 成员基类
│ ├── FieldExpression
│ └── PropertyExpression
├── BinaryExpression // 二元运算
├── UnaryExpression // 一元运算
├── ConvertExpression // (T)expr
├── TypeIsExpression // expr is T
├── TypeAsExpression // expr as T
├── CoalesceExpression // expr ?? fallback
├── ConditionExpression // 三目
├── MethodCallExpression // obj.Method(args)
├── InvocationExpression // MethodBase.Invoke(obj, object[])
├── NewExpression // new T(args)
├── NewArrayExpression // new T[n]
├── ArrayExpression // new T[]{ ... }
├── ArrayIndexExpression // arr[i](CanWrite=true)
├── ArrayLengthExpression // arr.Length
├── MemberInitExpression // new T{ P = v, ... }
├── MemberAssignment // 成员绑定
├── ReturnExpression // return
├── ThrowExpression // throw
├── GotoExpression // goto
├── LabelExpression // label:
├── BreakExpression // break
├── ContinueExpression // continue
└── BlockExpression // 语句块
├── LoopExpression // while(true){...}
├── ForEachExpression // for / foreach 统一循环
├── IfThenExpression
├── IfThenElseExpression
├── SwitchExpression
├── TryExpression
└── MethodEmitter
工厂方法分组
下文每条 API 都按 签名 → 约束 → 示例(必要时) 三段格式给出。
1. 常量与默认值
ConstantExpression Constant(object value);
ConstantExpression Constant(object value, Type constantType);
DefaultExpression Default(Type defaultType);
约束:
Constant(object value)自动推断类型:null → typeof(object),MethodInfo → typeof(MethodInfo),Type → typeof(Type)。ConstantExpression.IsNull == true表示该常量为null。- 支持值类型、引用类型、
Type对象、MethodInfo对象。
2. 变量与参数
VariableExpression Variable(Type variableType);
ParameterExpression Paramter(ParameterInfo parameter); // 注意:方法名拼写为 Paramter
约束:
VariableExpression首次出现在Load(ILGenerator)时才分配LocalBuilder,因此同一实例不能跨方法体使用。Paramter仅用于包装已有的ParameterInfo;通过MethodEmitter.DefineParameter得到的ParameterEmitter已经是ParameterExpression。
3. this / base
Expression This(AbstractTypeEmitter typeEmitter);
// base:先取 This,再访问 .Base
var thisExpr = (ThisExpression)Expression.This(cls);
Expression baseExpr = thisExpr.Base;
约束:
This不支持抽象类型。Base不支持值类型(值类型无基类引用语义)。
4. 字段与属性
FieldExpression Field(FieldInfo field); // static
FieldExpression Field(Expression instance, FieldInfo field); // instance
PropertyExpression Property(PropertyInfo property); // static
PropertyExpression Property(Expression instance, PropertyInfo property); // instance
约束:
- 静态成员重载要求
field.IsStatic == true/property的访问器为静态。 FieldEmitter与PropertyEmitter本身就是表达式,可直接使用,无需再包一层Field(...)/Property(...)。
5. 赋值
Expression Assign(Expression left, Expression right);
约束(任一满足即合法):
left.RuntimeType.IsAssignableFrom(right.RuntimeType);right是ThisExpression;- 两侧枚举的底层类型相同;
right.RuntimeType == Nullable.GetUnderlyingType(left.RuntimeType)。
left.CanWrite 必须为 true。
6. 类型转换与检查
ConvertExpression Convert(Expression body, Type convertToType); // (T)expr
TypeIsExpression TypeIs(Expression body, Type bodyIsType); // expr is T -> bool
TypeAsExpression TypeAs(Expression body, Type bodyAsType); // expr as T
CoalesceExpression Coalesce(Expression left, Expression right); // left ?? right
约束:
Convert:body.RuntimeType == convertToType时为零成本(不发射 IL)。Coalesce:left.RuntimeType必须为引用类型或可空值类型。
7. 三目
ConditionExpression Condition(Expression test, Expression ifTrue, Expression ifFalse);
ConditionExpression Condition(Expression test, Expression ifTrue, Expression ifFalse, Type returnType);
约束:
test.RuntimeType == typeof(bool)。- 不指定
returnType时,ifTrue与ifFalse必须类型相同; - 指定
returnType时,两分支必须可隐式转换为该类型,否则抛ArgumentException。
8. 方法调用 — 三种形态语义不同
| 工厂 | IL | 参数 | 适用 |
|---|---|---|---|
Call |
callvirt(virtual)/ call(其它) |
逐个 Expression |
编译期已知签名的常规调用 |
DeclaringCall |
接口/抽象 → callvirt,其它 → call |
逐个 Expression |
显式调用声明类型实现,绕过子类重写 |
Invoke |
MethodBase.Invoke(object, object[]) |
单个 object[] 表达式 |
运行期动态参数或动态方法 |
// Call
Expression.Call(methodInfo); // 静态无参
Expression.Call(methodInfo, arg0, arg1); // 静态有参
Expression.Call(instance, methodInfo); // 实例无参
Expression.Call(instance, methodInfo, arg0, arg1); // 实例有参
Expression.Call(methodEmitter); // 同模块 MethodEmitter
Expression.Call(methodEmitter, arg0, arg1);
// DeclaringCall
Expression.DeclaringCall(instance, methodInfo);
Expression.DeclaringCall(instance, methodInfo, arg0, arg1);
// Invoke(参数为 object[] 表达式)
Expression.Invoke(methodInfo, argsArrayExpr);
Expression.Invoke(instance, methodInfo, argsArrayExpr);
Expression.Invoke(methodEmitter, argsArrayExpr);
Expression.Invoke(instance, methodEmitter, argsArrayExpr);
Expression.Invoke(methodExpr, argsArrayExpr); // 动态 MethodInfo 表达式
Expression.Invoke(instance, methodExpr, argsArrayExpr);
Call vs DeclaringCall:Call 对 virtual 方法发 callvirt(多态分派);DeclaringCall 对非抽象非接口方法发 call(直接调用声明类型实现,绕过子类重写)。
9. 创建对象
NewExpression New(Type instanceType);
NewExpression New(ConstructorInfo constructor);
NewExpression New(Type instanceType, params Expression[] parameters);
NewExpression New(ConstructorInfo constructor, params Expression[] parameters);
Expression New(ConstructorEmitter constructorEmitter);
Expression New(ConstructorEmitter constructorEmitter, params Expression[] parameters);
10. 对象初始化器
MemberAssignment Bind(MemberInfo member, Expression expression);
MemberInitExpression MemberInit(NewExpression newExpression, params MemberAssignment[] bindings);
MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberAssignment> bindings);
约束:Bind 支持 FieldInfo 与 PropertyInfo(属性需有 setter)。
11. 数组
NewArrayExpression NewArray(int size); // new object[n]
NewArrayExpression NewArray(int size, Type elementType); // new T[n]
ArrayExpression Array(params Expression[] arguments); // new object[]{ ... }
ArrayExpression Array(Type elementType, params Expression[] arguments);
ArrayIndexExpression ArrayIndex(Expression array, int index); // arr[i],CanWrite=true
ArrayIndexExpression ArrayIndex(Expression array, Expression index); // arr[i],CanWrite=true
ArrayLengthExpression ArrayLength(Expression array); // arr.Length
12. 二元运算
算术(返回操作数同类型,支持 op_Addition 等用户重载)
| 方法 | 等价 |
|---|---|
Add(l, r) |
l + r |
AddChecked(l, r) |
checked(l + r)(add.ovf) |
AddAssign(l, r) |
l += r |
AddAssignChecked(l, r) |
checked(l += r) |
Subtract / SubtractChecked / SubtractAssign / SubtractAssignChecked |
减法系列 |
Multiply / MultiplyChecked / MultiplyAssign / MultiplyAssignChecked |
乘法系列 |
Divide(l, r) |
l / r |
DivideAssign(l, r) |
l /= r |
Modulo(l, r) |
l % r |
ModuloAssign(l, r) |
l %= r |
*Checked系列会发射add.ovf/sub.ovf/mul.ovf,溢出时抛OverflowException。仅在需要溢出检测时使用。
比较(返回 bool)
| 方法 | 等价 |
|---|---|
Equal(l, r) |
l == r |
NotEqual(l, r) |
l != r |
LessThan(l, r) |
l < r |
LessThanOrEqual(l, r) |
l <= r |
GreaterThan(l, r) |
l > r |
GreaterThanOrEqual(l, r) |
l >= r |
短路逻辑(返回 bool)
| 方法 | 等价 |
|---|---|
AndAlso(l, r) |
l && r |
OrElse(l, r) |
l \|\| r |
位运算(返回操作数同类型)
| 方法 | 等价 |
|---|---|
And(l, r) / AndAssign(l, r) |
l & r / l &= r |
Or(l, r) / OrAssign(l, r) |
l \| r / l \|= r |
ExclusiveOr(l, r) / ExclusiveOrAssign(l, r) |
l ^ r / l ^= r |
13. 一元运算
| 方法 | 等价 | 说明 |
|---|---|---|
Not(body) |
!x 或 ~x |
布尔逻辑反 / 整数按位补 |
IsFalse(body) |
!x |
仅用于布尔假值判断 |
Negate(body) |
-x |
取负 |
Increment(body) |
x + 1 |
生成增量值,不写回变量 |
Decrement(body) |
x - 1 |
生成减量值,不写回变量 |
IncrementAssign(body) |
++x |
写回变量,body.CanWrite == true |
DecrementAssign(body) |
--x |
写回变量,body.CanWrite == true |
IncrementvsIncrementAssign是常见易错点:前者只读,后者写回。
BlockExpression — 代码块
| 成员 | 类型 | 说明 |
|---|---|---|
Expression.Block() |
工厂 | 创建独立空块 |
IsEmpty |
bool |
块内无任何表达式 |
IsClosed |
bool |
最后一条为 Return / Goto / Break / Continue / Throw |
Append(expression) |
BlockExpression |
追加表达式,返回自身 |
var block = Expression.Block();
block.Append(Expression.Assign(varX, Expression.Constant(1)));
block.Append(Expression.IncrementAssign(varX));
method.Append(block); // 一旦 Append 进父块,block 即只读
自动 Pop:若块本身 IsVoid,追加非 void 表达式时框架自动插入 Nop 弹栈,确保 IL 栈平衡。
标签与跳转
Label label = Expression.Label(); // 创建 Goto 类型标签
method.Append(Expression.Label(label)); // 标记位置(LabelExpression)
method.Append(Expression.Goto(label)); // 跳转(GotoExpression)
LabelKind:
| 类型 | 来源 | 用途 |
|---|---|---|
Goto |
Expression.Label() |
手动无条件跳转 |
Break |
LoopExpression / ForEachExpression 内部 |
跳出循环 |
Continue |
LoopExpression / ForEachExpression 内部 |
跳回循环头 |
Return |
MethodEmitter 内部 |
方法出口 |
Break/Continue/Return标签由框架自动管理,开发者只需创建Goto类型标签。
Return — 方法返回
Expression.Return(); // void 方法
Expression.Return(valueExpr); // 有返回值
约束:必须出现在 MethodEmitter 的作用域链内。框架在 Emit 阶段自动绑定到方法出口,发射 br 跳转到 ret 之前。
Loop — 循环
LoopExpression loop = Expression.Loop(); // while(true){ ... }
loop.Append(Expression.IfThen(
Expression.Equal(counter, Expression.Constant(10)),
Expression.Break()));
loop.Append(Expression.IncrementAssign(counter));
loop.Append(Expression.Continue()); // 可省略,循环末尾自动跳回头部
method.Append(loop);
约束:
loop.IsEmpty时调用Load抛AstException;BreakExpression/ContinueExpression仅可出现在某个循环块的Append链中;对LoopExpression而言即当前loop内部。
ForEach — 统一遍历循环
var sum = Expression.Variable(typeof(int));
var item = Expression.Variable(typeof(int));
method.Append(Expression.Assign(sum, Expression.Constant(0)));
ForEachExpression loop = Expression.ForEach(item, sourceExpr);
loop.Append(Expression.AddAssign(sum, item));
method.Append(loop);
method.Append(sum);
签名
ForEachExpression ForEach(VariableExpression item, Expression source)
选择策略
Expression.ForEach(item, source) 会按以下优先级决定生成哪类循环:
- 索引循环(优先于枚举器)
source为一维数组;或source具备int Count/int Length属性以及this[int]索引器,且索引器返回类型与item.RuntimeType完全一致。
- 泛型枚举器循环
- 若索引循环不适用,则优先查找返回元素类型与
item.RuntimeType完全一致的IEnumerable<T>。
- 非泛型枚举器循环
- 仅当
source不暴露任何IEnumerable<T>、但实现了IEnumerable时才回退;此时每轮迭代会把IEnumerator.Current强转为item.RuntimeType。
类型约束
- 数组路径要求
item.RuntimeType == elementType,否则构造时抛AstException。 - 索引器路径要求
this[int]返回类型与item.RuntimeType完全一致。 - 若
source已暴露某个IEnumerable<T>,但不存在T == item.RuntimeType的精确匹配,则不会降级到IEnumerable路径,而是直接抛AstException。 - 非泛型
IEnumerable路径下:item.RuntimeType == typeof(object)时直接赋值;- 值类型生成
unbox.any; - 引用类型生成
castclass,运行时不匹配将抛标准类型转换异常。
控制流语义
ForEachExpression继承自BlockExpression,通过Append(...)追加循环体。- 支持在循环体内部使用
Expression.Break()与Expression.Continue()。 - 与
LoopExpression一样,仅允许Return标签向外冒泡。
示例
var item = Expression.Variable(typeof(int));
var loop = Expression.ForEach(item, numbersExpr);
loop.Append(Expression.IfThen(
Expression.Equal(item, Expression.Constant(0)),
Expression.Continue()));
loop.Append(Expression.IfThen(
Expression.GreaterThan(item, Expression.Constant(100)),
Expression.Break()));
loop.Append(Expression.AddAssign(total, item));
Switch — 分支
SwitchExpression 在构造时根据 switchValue 类型确定模式,之后不可切换。
模式 A:算术值比较(整数 / 枚举 / char)
var sw = Expression.Switch(counter); // 无 default
var sw = Expression.Switch(counter, defaultBodyExpr); // 有 default
sw.Case(Expression.Constant(1))
.Append(Expression.Call(method1))
.Append(Expression.Break()); // 不加 Break 则 fallthrough
sw.Case(Expression.Constant(2))
.Append(Expression.Call(method2));
method.Append(sw);
模式 B:引用类型匹配(is T,类似 C# 8 模式 switch)
var sw = Expression.Switch(animalExpr);
var dogVar = Expression.Variable(typeof(Dog)); // 匹配成功后自动赋值
sw.Case(dogVar).Append(Expression.Call(dogVar, barkMethod));
var catVar = Expression.Variable(typeof(Cat));
sw.Case(catVar).Append(Expression.Call(catVar, meowMethod));
method.Append(sw);
不可混用:
Case(ConstantExpression)与Case(VariableExpression)不能在同一SwitchExpression中混用,否则抛AstException。
Try-Catch-Finally — 异常处理
TryExpression 继承自 BlockExpression,try 块内容用 Append,catch / finally 用专用方法。
// try { } finally { ... }
var t1 = Expression.Try(finallyBodyExpr);
t1.Append(riskyOperation);
// try { } catch(Exception) { ... }
var t2 = Expression.Try();
t2.Append(riskyOperation);
t2.Catch().Append(Expression.Call(logMethod));
// catch 指定异常类型
t2.Catch(typeof(InvalidOperationException))
.Append(recoveryExpr);
// catch 并绑定到变量(catch(InvalidOperationException ex))
var ex = Expression.Variable(typeof(InvalidOperationException));
t2.Catch(ex)
.Append(Expression.Property(ex, typeof(Exception).GetProperty("Message")))
.Append(Expression.Call(logExMethod, ex));
method.Append(t2);
API 速查:
| 方法 | 说明 |
|---|---|
Expression.Try() |
无 finally |
Expression.Try(Expression finallyExpr) |
带 finally |
tryExpr.Catch() |
捕获 Exception |
tryExpr.Catch(Type) |
捕获指定异常类型 |
tryExpr.Catch(VariableExpression) |
捕获并赋值给变量(变量类型须继承 Exception) |
IErrorHandler.Append(expr) |
向 catch 块追加;返回自身以链式调用 |
完整示例:生成 AOP 代理方法
目标等价 C#:
public class Proxy : CalculatorBase { private readonly ICalculator _impl; public Proxy(ICalculator impl) { _impl = impl; } public override int Add(int a, int b) { try { return _impl.Add(a, b); } catch (Exception ex) { throw; } } }
var module = new ModuleEmitter("MyProxy");
var cls = module.DefineType(
"Proxy.Calculator",
TypeAttributes.Public | TypeAttributes.Class,
typeof(CalculatorBase));
// 字段
var implField = cls.DefineField("_impl", typeof(ICalculator), FieldAttributes.Private);
// 构造函数
var ctor = cls.DefineConstructor(MethodAttributes.Public);
var implParam = ctor.DefineParameter(typeof(ICalculator), ParameterAttributes.None, "impl");
ctor.Append(Expression.Assign(implField, implParam));
// 重写 Add
var addMethod = cls.OverrideMethod(typeof(CalculatorBase).GetMethod("Add"));
var paramA = addMethod.GetParameters()[0];
var paramB = addMethod.GetParameters()[1];
var ex = Expression.Variable(typeof(Exception));
var tryExpr = Expression.Try();
tryExpr.Append(Expression.Return(
Expression.Call(implField, typeof(ICalculator).GetMethod("Add"), paramA, paramB)));
tryExpr.Catch(ex).Append(Expression.Throw(ex));
addMethod.Append(tryExpr);
Type proxyType = cls.CreateType();
行为约束 / 常见陷阱(Cheatsheet)
| 事项 | 规则 |
|---|---|
BlockExpression 只读 |
一旦被 Append 进父块,自身变只读,再追加抛 AstException。需要复用应先构建完毕再嵌入 |
VariableExpression 跨方法 |
同一实例只能在一个 MethodEmitter 中使用;每个方法体独立 Expression.Variable(...) |
| 泛型方法调用 | 调用前必须 MakeGenericMethod,否则 MethodCallExpression 构造时抛异常 |
Return 位置 |
仅可出现在 MethodEmitter 作用域链;框架自动绑定出口标签 |
Switch 模式 |
构造即决定,Case(ConstantExpression) / Case(VariableExpression) 不可混用 |
| 溢出检查 | 仅 *Checked 系列发射 *.ovf 指令;其它算术不做溢出检测 |
Call vs DeclaringCall |
virtual 方法 Call 发 callvirt(多态);DeclaringCall 发 call(绑定声明类型) |
Increment vs IncrementAssign |
前者不写回变量,仅产生新值;后者写回,要求 CanWrite == true |
| 赋值类型兼容 | 见第 5 节四条规则,任一满足即可 |
Coalesce 类型 |
左侧必须为引用类型或 Nullable<T> |
配套生态
- Inkslab.Intercept — 基于本库构建的 AOP 拦截框架,通过返回值类型选择拦截器,零侵入接入 ASP.NET Core DI。
许可证
| Product | Versions 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. |
-
.NETFramework 4.6.1
- No dependencies.
-
.NETStandard 2.1
- No dependencies.
-
net6.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Inkslab.Expressions:
| Package | Downloads |
|---|---|
|
Inkslab.Intercept
AOP framework based on method return types. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.12 | 116 | 4/28/2026 |
| 1.2.10 | 122 | 4/24/2026 |
| 1.2.8 | 327 | 11/14/2025 |
| 1.2.7 | 197 | 10/10/2025 |
| 1.2.6 | 1,316 | 7/11/2024 |
| 1.2.4 | 921 | 7/5/2024 |
| 1.2.3 | 257 | 7/5/2024 |
| 1.2.2 | 282 | 7/4/2024 |
| 1.2.1 | 294 | 7/4/2024 |
| 1.2.0.2 | 5,104 | 10/30/2023 |
| 1.2.0.1 | 234 | 10/30/2023 |
| 1.2.0 | 269 | 10/30/2023 |
| 1.1.0 | 298 | 10/21/2023 |
| 1.0.2.1 | 281 | 10/21/2023 |