Skip to content

类型信息获取详解

📚 文档导航

本文档详细介绍如何使用语义模型获取和处理类型信息。

📖 文档系列

文档内容难度
语义模型基础基础概念、快速入门、核心功能🟢 入门
类型信息获取GetTypeInfo、常量值、TypeInfo 详解🟡 中级
符号信息获取GetDeclaredSymbol、GetSymbolInfo、符号遍历🟡 中级
高级主题泛型、继承、特性、类型转换🔴 高级
最佳实践性能优化、常见问题、实战场景🟡 中级

GetTypeInfo 方法

GetTypeInfo 是获取表达式类型信息的核心方法。

💡 核心概念

GetTypeInfo 返回一个 TypeInfo 结构,包含:

  • Type: 表达式的原始类型
  • ConvertedType: 表达式转换后的类型

基础用法

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

public class TypeInfoExamples
{
    public void DemonstrateGetTypeInfo()
    {
        var code = @"
            class Calculator
            {
                public int Add(int a, int b)
                {
                    var result = a + b;
                    return result;
                }
                
                public void Test()
                {
                    var x = 5;
                    var y = 10;
                    var sum = Add(x, y);
                    
                    object obj = ""hello"";
                    string str = (string)obj;
                }
            }
        ";
        
        var tree = CSharpSyntaxTree.ParseText(code);
        var compilation = CreateCompilation(tree);
        var model = compilation.GetSemanticModel(tree);
        var root = tree.GetRoot();
        
        // 1. 获取变量声明的类型
        var variableDeclarations = root.DescendantNodes()
            .OfType<VariableDeclarationSyntax>();
        
        foreach (var varDecl in variableDeclarations)
        {
            var variable = varDecl.Variables.First();
            if (variable.Initializer != null)
            {
                var typeInfo = model.GetTypeInfo(variable.Initializer.Value);
                
                Console.WriteLine($"变量: {variable.Identifier.Text}");
                Console.WriteLine($"  类型: {typeInfo.Type?.ToDisplayString()}");
                Console.WriteLine($"  转换后类型: {typeInfo.ConvertedType?.ToDisplayString()}");
                Console.WriteLine();
            }
        }
        
        // 2. 获取方法调用的类型
        var invocations = root.DescendantNodes()
            .OfType<InvocationExpressionSyntax>();
        
        foreach (var invocation in invocations)
        {
            var typeInfo = model.GetTypeInfo(invocation);
            
            Console.WriteLine($"方法调用: {invocation.Expression}");
            Console.WriteLine($"  返回类型: {typeInfo.Type?.ToDisplayString()}");
            Console.WriteLine();
        }
        
        // 3. 获取类型转换的信息
        var casts = root.DescendantNodes()
            .OfType<CastExpressionSyntax>();
        
        foreach (var cast in casts)
        {
            var typeInfo = model.GetTypeInfo(cast.Expression);
            var targetType = model.GetTypeInfo(cast.Type);
            
            Console.WriteLine($"类型转换: {cast}");
            Console.WriteLine($"  源类型: {typeInfo.Type?.ToDisplayString()}");
            Console.WriteLine($"  目标类型: {targetType.Type?.ToDisplayString()}");
            Console.WriteLine();
        }
    }
    
    private Compilation CreateCompilation(SyntaxTree tree)
    {
        return CSharpCompilation.Create("temp")
            .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
            .AddSyntaxTrees(tree);
    }
}

输出示例

变量: result
  类型: System.Int32
  转换后类型: System.Int32

变量: x
  类型: System.Int32
  转换后类型: System.Int32

变量: y
  类型: System.Int32
  转换后类型: System.Int32

变量: sum
  类型: System.Int32
  转换后类型: System.Int32

方法调用: Add
  返回类型: System.Int32

类型转换: (string)obj
  源类型: System.Object
  目标类型: System.String

TypeInfo 结构详解

TypeInfo 结构包含两个重要属性:

属性说明示例
Type表达式的原始类型int
ConvertedType表达式转换后的类型long(如果发生了隐式转换)

隐式转换示例

csharp
public class TypeInfoStructure
{
    public void ExplainTypeInfo()
    {
        var code = @"
            class Example
            {
                public void Method()
                {
                    int x = 5;
                    long y = x;  // 隐式转换
                    
                    object obj = ""hello"";
                    string str = obj as string;  // as 转换
                }
            }
        ";
        
        var tree = CSharpSyntaxTree.ParseText(code);
        var compilation = CreateCompilation(tree);
        var model = compilation.GetSemanticModel(tree);
        var root = tree.GetRoot();
        
        // 分析隐式转换
        var assignments = root.DescendantNodes()
            .OfType<AssignmentExpressionSyntax>();
        
        foreach (var assignment in assignments)
        {
            var rightTypeInfo = model.GetTypeInfo(assignment.Right);
            var leftTypeInfo = model.GetTypeInfo(assignment.Left);
            
            Console.WriteLine($"赋值: {assignment}");
            Console.WriteLine($"  右侧类型: {rightTypeInfo.Type?.ToDisplayString()}");
            Console.WriteLine($"  右侧转换后类型: {rightTypeInfo.ConvertedType?.ToDisplayString()}");
            Console.WriteLine($"  左侧类型: {leftTypeInfo.Type?.ToDisplayString()}");
            
            // 检查是否发生了类型转换
            if (rightTypeInfo.Type?.ToDisplayString() != rightTypeInfo.ConvertedType?.ToDisplayString())
            {
                Console.WriteLine($"  ⚠️ 发生了隐式转换");
            }
            
            Console.WriteLine();
        }
    }
    
    private Compilation CreateCompilation(SyntaxTree tree)
    {
        return CSharpCompilation.Create("temp")
            .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
            .AddSyntaxTrees(tree);
    }
}

⚠️ 注意

TypeConvertedType 不同时,说明发生了隐式类型转换。


获取常量值

使用 GetConstantValue 方法可以获取编译时常量的值。

基础用法

csharp
public class ConstantValueExamples
{
    public void GetConstantValues()
    {
        var code = @"
            class Constants
            {
                public const int MaxValue = 100;
                public const string Name = ""John"";
                public const bool IsEnabled = true;
                
                public void Method()
                {
                    var x = 5 + 10;  // 编译时常量
                    var y = MaxValue * 2;  // 编译时常量
                    var z = DateTime.Now;  // 非常量
                }
            }
        ";
        
        var tree = CSharpSyntaxTree.ParseText(code);
        var compilation = CreateCompilation(tree);
        var model = compilation.GetSemanticModel(tree);
        var root = tree.GetRoot();
        
        // 1. 获取常量字段的值
        var fields = root.DescendantNodes()
            .OfType<FieldDeclarationSyntax>();
        
        foreach (var field in fields)
        {
            var variable = field.Declaration.Variables.First();
            if (variable.Initializer != null)
            {
                var constantValue = model.GetConstantValue(variable.Initializer.Value);
                
                Console.WriteLine($"字段: {variable.Identifier.Text}");
                Console.WriteLine($"  是常量: {constantValue.HasValue}");
                if (constantValue.HasValue)
                {
                    Console.WriteLine($"  值: {constantValue.Value}");
                }
                Console.WriteLine();
            }
        }
        
        // 2. 获取表达式的常量值
        var variableDeclarations = root.DescendantNodes()
            .OfType<VariableDeclarationSyntax>()
            .Where(v => v.Parent is LocalDeclarationStatementSyntax);
        
        foreach (var varDecl in variableDeclarations)
        {
            var variable = varDecl.Variables.First();
            if (variable.Initializer != null)
            {
                var constantValue = model.GetConstantValue(variable.Initializer.Value);
                
                Console.WriteLine($"变量: {variable.Identifier.Text}");
                Console.WriteLine($"  初始化表达式: {variable.Initializer.Value}");
                Console.WriteLine($"  是编译时常量: {constantValue.HasValue}");
                if (constantValue.HasValue)
                {
                    Console.WriteLine($"  值: {constantValue.Value}");
                }
                Console.WriteLine();
            }
        }
    }
    
    private Compilation CreateCompilation(SyntaxTree tree)
    {
        return CSharpCompilation.Create("temp")
            .AddReferences(
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(DateTime).Assembly.Location)
            )
            .AddSyntaxTrees(tree);
    }
}

输出示例

字段: MaxValue
  是常量: True
  值: 100

字段: Name
  是常量: True
  值: John

字段: IsEnabled
  是常量: True
  值: True

变量: x
  初始化表达式: 5 + 10
  是编译时常量: True
  值: 15

变量: y
  初始化表达式: MaxValue * 2
  是编译时常量: True
  值: 200

变量: z
  初始化表达式: DateTime.Now
  是编译时常量: False

💡 提示

编译时常量包括:

  • 字面量(5, "hello", true
  • const 字段
  • 常量表达式(5 + 10, MaxValue * 2

类型比较

使用 SymbolEqualityComparer

csharp
public class TypeComparison
{
    public void CompareTypes()
    {
        var code = @"
            class Person { }
            class Employee : Person { }
            
            class TypeTests
            {
                public void Test()
                {
                    Person p = new Person();
                    Employee e = new Employee();
                    
                    var list1 = new List<int>();
                    var list2 = new List<int>();
                    var list3 = new List<string>();
                }
            }
        ";
        
        var tree = CSharpSyntaxTree.ParseText(code);
        var compilation = CreateCompilation(tree);
        var model = compilation.GetSemanticModel(tree);
        var root = tree.GetRoot();
        
        // 获取所有变量声明
        var variables = root.DescendantNodes()
            .OfType<VariableDeclarationSyntax>()
            .SelectMany(v => v.Variables)
            .ToList();
        
        // 比较类型
        for (int i = 0; i < variables.Count; i++)
        {
            for (int j = i + 1; j < variables.Count; j++)
            {
                var symbol1 = model.GetDeclaredSymbol(variables[i]);
                var symbol2 = model.GetDeclaredSymbol(variables[j]);
                
                if (symbol1?.Type is ITypeSymbol t1 && symbol2?.Type is ITypeSymbol t2)
                {
                    Console.WriteLine($"比较: {variables[i].Identifier.Text} vs {variables[j].Identifier.Text}");
                    Console.WriteLine($"  类型相等: {SymbolEqualityComparer.Default.Equals(t1, t2)}");
                    Console.WriteLine($"  {variables[i].Identifier.Text} 继承自 {variables[j].Identifier.Text}: {IsInheritedFrom(t1, t2)}");
                    Console.WriteLine($"  {variables[j].Identifier.Text} 继承自 {variables[i].Identifier.Text}: {IsInheritedFrom(t2, t1)}");
                    Console.WriteLine();
                }
            }
        }
    }
    
    private bool IsInheritedFrom(ITypeSymbol derived, ITypeSymbol baseType)
    {
        var current = derived.BaseType;
        while (current != null)
        {
            if (SymbolEqualityComparer.Default.Equals(current, baseType))
            {
                return true;
            }
            current = current.BaseType;
        }
        return false;
    }
    
    private Compilation CreateCompilation(SyntaxTree tree)
    {
        return CSharpCompilation.Create("temp")
            .AddReferences(
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(List<>).Assembly.Location)
            )
            .AddSyntaxTrees(tree);
    }
}

⚠️ 重要

永远使用 SymbolEqualityComparer.Default.Equals() 来比较类型,不要使用 ==Equals()


实用工具方法

检查类型种类

csharp
public static class TypeInfoHelpers
{
    // 检查是否是值类型
    public static bool IsValueType(ITypeSymbol type)
    {
        return type.IsValueType;
    }
    
    // 检查是否是引用类型
    public static bool IsReferenceType(ITypeSymbol type)
    {
        return type.IsReferenceType;
    }
    
    // 检查是否是可空类型
    public static bool IsNullable(ITypeSymbol type)
    {
        return type is INamedTypeSymbol namedType &&
               namedType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T;
    }
    
    // 获取可空类型的底层类型
    public static ITypeSymbol GetUnderlyingType(ITypeSymbol type)
    {
        if (type is INamedTypeSymbol namedType &&
            namedType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
        {
            return namedType.TypeArguments[0];
        }
        return type;
    }
    
    // 检查是否是数组类型
    public static bool IsArray(ITypeSymbol type)
    {
        return type.TypeKind == TypeKind.Array;
    }
    
    // 检查是否是集合类型
    public static bool IsCollection(ITypeSymbol type, Compilation compilation)
    {
        var ienumerableType = compilation.GetTypeByMetadataName("System.Collections.IEnumerable");
        if (ienumerableType == null) return false;
        
        return type.AllInterfaces.Any(i => 
            SymbolEqualityComparer.Default.Equals(i.OriginalDefinition, ienumerableType));
    }
}

常见场景

场景 1: 检查变量类型

csharp
public bool IsStringVariable(VariableDeclaratorSyntax variable, SemanticModel model)
{
    var symbol = model.GetDeclaredSymbol(variable);
    if (symbol is ILocalSymbol localSymbol)
    {
        return localSymbol.Type.SpecialType == SpecialType.System_String;
    }
    return false;
}

场景 2: 获取方法返回类型

csharp
public ITypeSymbol GetMethodReturnType(MethodDeclarationSyntax method, SemanticModel model)
{
    var methodSymbol = model.GetDeclaredSymbol(method) as IMethodSymbol;
    return methodSymbol?.ReturnType;
}

场景 3: 检查表达式是否可以为 null

csharp
public bool CanBeNull(ExpressionSyntax expression, SemanticModel model)
{
    var typeInfo = model.GetTypeInfo(expression);
    var type = typeInfo.Type;
    
    if (type == null) return true;
    
    // 引用类型可以为 null
    if (type.IsReferenceType) return true;
    
    // 可空值类型可以为 null
    if (type is INamedTypeSymbol namedType &&
        namedType.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T)
    {
        return true;
    }
    
    return false;
}

下一步


🔗 相关资源


最后更新: 2025-01-21

基于 MIT 许可发布