类型信息获取详解
📚 文档导航
本文档详细介绍如何使用语义模型获取和处理类型信息。
📖 文档系列
| 文档 | 内容 | 难度 |
|---|---|---|
| 语义模型基础 | 基础概念、快速入门、核心功能 | 🟢 入门 |
| 类型信息获取 ⭐ | 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.StringTypeInfo 结构详解
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);
}
}⚠️ 注意
当 Type 和 ConvertedType 不同时,说明发生了隐式类型转换。
获取常量值
使用 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;
}下一步
🔗 相关资源
- 语义模型基础 - 返回主文档
- 类型系统深入 - 类型系统详细讲解
- TypeInfo API(官方文档)
最后更新: 2025-01-21