You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何分析LINQ谓词生成的Predicate`1类型FieldInfo引用的成员

实现分析逻辑的两种方案

方案1:调整代码改用表达式树(最简便,适合可修改业务代码的场景)

如果你可以修改传入FindAll的参数类型,不需要直接用Predicate<T>委托,而是改用Expression<Predicate<T>>,可以直接遍历表达式树节点拿到所有引用的成员,不需要处理底层IL。
示例代码逻辑:

Expression<Predicate<YourType>> expr = m => !m.ArchName().Contains("<>c");
// 自定义表达式访问器遍历所有成员引用
class MemberAccessVisitor : ExpressionVisitor
{
    public List<MemberInfo> ReferencedMembers { get; } = new();
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        ReferencedMembers.Add(node.Method);
        return base.VisitMethodCall(node);
    }
    protected override Expression VisitMember(MemberExpression node)
    {
        ReferencedMembers.Add(node.Member);
        return base.VisitMember(node);
    }
}
// 使用访问器
var visitor = new MemberAccessVisitor();
visitor.Visit(expr);
// 此时visitor.ReferencedMembers里就包含了ArchName、String.Contains这些引用的方法
// 要转成Predicate委托直接调用Compile()即可
var predicate = expr.Compile();
var result = list.FindAll(predicate);

这种方案不需要处理编译器生成的闭包类、FieldInfo相关的底层逻辑,可靠性最高。

方案2:从FieldInfo对应的委托实例分析IL(适合无法修改业务代码、只能拿到编译后字段的场景)

你拿到的Predicate1类型的FieldInfo,实际存储的是委托实例,你需要先拿到这个委托的MethodInfo`,再解析方法体的IL指令,提取所有引用的成员:

具体步骤

  • 第一步:从FieldInfo拿到委托实例,再取目标方法
// 假设fieldInfo是你拿到的Predicate`1类型的字段,instance是闭包类实例(静态字段的话instance传null)
Delegate predicateDel = (Delegate)fieldInfo.GetValue(instance);
MethodInfo lambdaMethod = predicateDel.Method;
  • 第二步:解析lambdaMethod的方法体IL,遍历所有成员引用指令
    IL中引用成员的指令包括call、callvirt、ldfld、ldsfld、stfld、stsfld、ldtoken等,这些指令的操作数就是对应的MemberInfo。
    你可以用现成的IL解析库(比如System.Reflection.Metadata)来做解析,也可以自己实现基础的IL读取逻辑,示例逻辑如下:
byte[] ilBytes = lambdaMethod.GetMethodBody().GetILAsByteArray();
Module module = lambdaMethod.Module;
List<MemberInfo> referencedMembers = new List<MemberInfo>();
using (var reader = new BinaryReader(new MemoryStream(ilBytes)))
{
    while (reader.BaseStream.Position < reader.BaseStream.Length)
    {
        OpCode opCode;
        byte op = reader.ReadByte();
        if (op == 0xFE)
        {
            opCode = (OpCode)BitConverter.ToInt16(new byte[] { reader.ReadByte(), op }, 0);
        }
        else
        {
            opCode = (OpCode)op;
        }
        // 处理调用方法、访问字段的相关指令
        if (opCode.OperandType is OperandType.InlineMethod or OperandType.InlineField or OperandType.InlineTok)
        {
            int token = reader.ReadInt32();
            MemberInfo member = module.ResolveMember(token);
            referencedMembers.Add(member);
        }
        else
        {
            // 跳过其他指令的操作数
            switch (opCode.OperandType)
            {
                case OperandType.InlineNone: break;
                case OperandType.InlineI: reader.ReadInt32(); break;
                case OperandType.InlineI8: reader.ReadInt64(); break;
                case OperandType.InlineR: reader.ReadDouble(); break;
                case OperandType.ShortInlineI: reader.ReadSByte(); break;
                case OperandType.ShortInlineR: reader.ReadSingle(); break;
                // 其余操作数类型可按需补充跳过逻辑
            }
        }
    }
}

解析后你只要过滤掉编译器生成的闭包类成员、系统基础类成员,剩下的就是你要找的用户自定义成员,比如示例里的ArchName方法。

注意点

  • 如果你拿到的FieldInfo是编译器生成的闭包类的字段,要注意闭包类的实例获取逻辑,静态lambda和捕获了外部变量的实例lambda的闭包存储逻辑不一样。
  • IL解析要注意处理不同指令的操作数长度,避免读取偏移错误。

内容的提问来源于stack exchange,提问作者Minho17

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 23:15:03