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

如何在Roslyn中获取无显式类型声明lambda的MemberAccess的TypeInfo

问题描述

例如我有如下代码:

using System.Linq;

namespace MyNamespace
{
    class Person
    {
        public string Name { get; set; }
    }
    
    class Program
    {
        static void Main()
        {
            string name = new Person().Name; // SemanticModel.GetTypeInfo() 返回string,结果符合预期
            string[] names = new Person[] { new Person() }.Select(p => p.Name).ToArray(); // SemanticModel.GetTypeInfo() 返回ErrorTypeSymbol
                     names = new Person[] { new Person() }.Select(Person p => p.Name).ToArray(); // SemanticModel.GetTypeInfo() 返回string,结果符合预期
        }
    }
}

我想要获取LINQ Select扩展方法的lambda表达式中p.Name对应的TypeInfo。我在自己的Roslyn分析器中编写了如下代码:

syntaxTree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().ToArray();

执行后得到如下结果:

[0] = {MemberAccessExpressionSyntax} new Person().Name
[1] = {MemberAccessExpressionSyntax} new Person[] { new Person() }.Select(p => p.Name).ToArray
[2] = {MemberAccessExpressionSyntax} new Person[] { new Person() }.Select
[3] = {MemberAccessExpressionSyntax} p.Name

随后我尝试获取第一个MemberAccessExpressionSyntax new Person().Name的TypeInfo:

context.Compilation.GetSemanticModel(syntaxTree).GetTypeInfo(syntaxTree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().ToArray()[0]).Type.Name

执行后返回String,结果符合预期。
但当我尝试获取Select(p => p.Name)的lambda表达式中的MemberAccessExpression节点的类型时:

context.Compilation.GetSemanticModel(syntaxTree).GetTypeInfo(syntaxTree.GetRoot().DescendantNodes().OfType<MemberAccessExpressionSyntax>().ToArray()[3]).Type.Name

返回的是空字符串。
请问如何从SemanticModel中获取无显式类型声明的lambda表达式中的MemberAccessExpressionSyntax对应的TypeInfo?


解答

问题原因

隐式类型lambda(参数未显式声明类型)的参数类型需要依赖其绑定的目标委托类型推导。你直接访问lambda内部的p.Name节点时,语义模型尚未完成从上层Select方法调用到lambda的绑定推导,所以无法得到正确的类型信息,返回空或ErrorTypeSymbol。

解决方法

你需要先绑定上层Select方法调用的语义,再推导lambda内部成员的类型,步骤如下:

  1. 找到p.Name所属的SimpleLambdaExpressionSyntax节点,再向上查找其父级的Select方法对应的InvocationExpressionSyntax节点
  2. 先获取Select方法调用的语义符号,完成上层调用的语义绑定
  3. 再获取lambda参数的声明符号,即可得到正确的参数类型,此时再访问内部成员的TypeInfo就能得到正确结果

实现代码

// 定位到目标p.Name节点
var targetMemberAccess = syntaxTree.GetRoot()
    .DescendantNodes()
    .OfType<MemberAccessExpressionSyntax>()
    .First(m => m.Name.Identifier.Text == "Name" 
                && m.Expression is IdentifierNameSyntax identifier 
                && identifier.Identifier.Text == "p");

var semanticModel = context.Compilation.GetSemanticModel(syntaxTree);
// 找到所属的lambda表达式
var lambda = targetMemberAccess.FirstAncestorOrSelf<SimpleLambdaExpressionSyntax>();
// 获取lambda参数的声明符号,上层调用绑定完成后可直接拿到正确的Person类型
var lambdaParamSymbol = semanticModel.GetDeclaredSymbol(lambda.Parameter);
// 此时再获取p.Name的TypeInfo就能得到正确的string类型
var nameTypeInfo = semanticModel.GetTypeInfo(targetMemberAccess);
Console.WriteLine(nameTypeInfo.Type?.Name); // 输出String

备选方案

你也可以直接通过参数类型查找对应成员的类型,可靠性更高,不需要依赖成员访问节点的TypeInfo返回:

var personType = lambdaParamSymbol.Type as INamedTypeSymbol;
var nameProperty = personType.GetMembers("Name")
    .OfType<IPropertySymbol>()
    .First();
Console.WriteLine(nameProperty.Type.Name); // 输出String

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 04:24:09