Skip to content

类型参数详解

简介

本文档详细介绍 ITypeParameterSymbol(类型参数符号),包括类型参数约束、泛型方法和类型、协变和逆变。

适合人群: 中级开发者
预计阅读时间: 35 分钟
前置知识: 语法树基础、符号系统、泛型概念


📋 本文导航

章节难度阅读时间链接
核心属性🟢8 分钟查看
类型参数约束🟡12 分钟查看
泛型方法和类型🟡10 分钟查看
协变和逆变🟡5 分钟查看

返回: 主文档 | 上一篇: 事件详解


🟢 核心属性

ITypeParameterSymbol 表示泛型类型参数,如 List<T> 中的 T

基本信息属性

csharp
ITypeParameterSymbol typeParameter;

// 类型参数名称(如 T, TKey, TValue)
string name = typeParameter.Name;

// 类型参数的序号(从 0 开始)
int ordinal = typeParameter.Ordinal;

// 类型参数的种类(始终是 TypeParameter)
TypeKind typeKind = typeParameter.TypeKind; // TypeKind.TypeParameter

// 是否是引用类型(根据约束判断)
bool isReferenceType = typeParameter.IsReferenceType;

// 是否是值类型(根据约束判断)
bool isValueType = typeParameter.IsValueType;

// 声明类型参数的符号(类或方法)
ISymbol declaringSymbol = typeParameter.DeclaringMethod ?? (ISymbol)typeParameter.DeclaringType;

约束属性

csharp
// 是否有 class 约束
bool hasReferenceTypeConstraint = typeParameter.HasReferenceTypeConstraint;

// 是否有 struct 约束
bool hasValueTypeConstraint = typeParameter.HasValueTypeConstraint;

// 是否有 new() 约束
bool hasConstructorConstraint = typeParameter.HasConstructorConstraint;

// 是否有 unmanaged 约束
bool hasUnmanagedTypeConstraint = typeParameter.HasUnmanagedTypeConstraint;

// 是否有 notnull 约束
bool hasNotNullConstraint = typeParameter.HasNotNullConstraint;

// 类型约束列表
ImmutableArray<ITypeSymbol> constraintTypes = typeParameter.ConstraintTypes;

协变和逆变属性

csharp
// 协变/逆变类型(None, Out, In)
VarianceKind variance = typeParameter.Variance;

// 是否是协变(out)
bool isCovariant = typeParameter.Variance == VarianceKind.Out;

// 是否是逆变(in)
bool isContravariant = typeParameter.Variance == VarianceKind.In;

示例:分析类型参数基本信息

csharp
public void AnalyzeTypeParameterBasicInfo(ITypeParameterSymbol typeParameter)
{
    Console.WriteLine($"类型参数名称: {typeParameter.Name}");
    Console.WriteLine($"序号: {typeParameter.Ordinal}");
    
    // 显示声明位置
    if (typeParameter.DeclaringMethod != null)
    {
        Console.WriteLine($"声明在方法: {typeParameter.DeclaringMethod.ToDisplayString()}");
    }
    else if (typeParameter.DeclaringType != null)
    {
        Console.WriteLine($"声明在类型: {typeParameter.DeclaringType.ToDisplayString()}");
    }
    
    // 显示约束
    Console.WriteLine($"\n约束:");
    if (typeParameter.HasReferenceTypeConstraint)
        Console.WriteLine("  - class (引用类型约束)");
    if (typeParameter.HasValueTypeConstraint)
        Console.WriteLine("  - struct (值类型约束)");
    if (typeParameter.HasConstructorConstraint)
        Console.WriteLine("  - new() (构造函数约束)");
    if (typeParameter.HasUnmanagedTypeConstraint)
        Console.WriteLine("  - unmanaged (非托管类型约束)");
    if (typeParameter.HasNotNullConstraint)
        Console.WriteLine("  - notnull (非空约束)");
    
    // 显示类型约束
    if (typeParameter.ConstraintTypes.Length > 0)
    {
        Console.WriteLine("  类型约束:");
        foreach (var constraint in typeParameter.ConstraintTypes)
        {
            Console.WriteLine($"    - {constraint.ToDisplayString()}");
        }
    }
    
    // 显示协变/逆变
    if (typeParameter.Variance != VarianceKind.None)
    {
        Console.WriteLine($"\n协变/逆变: {typeParameter.Variance}");
    }
}

提示

类型参数的 Ordinal 属性表示它在类型参数列表中的位置,从 0 开始。


🟡 类型参数约束

类型参数可以有多种约束,限制可以用作类型实参的类型。

引用类型约束(class)

csharp
/// <summary>
/// 分析引用类型约束
/// </summary>
public void AnalyzeReferenceTypeConstraint(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.HasReferenceTypeConstraint)
    {
        Console.WriteLine($"类型参数 {typeParameter.Name} 有 class 约束");
        Console.WriteLine("只能使用引用类型作为类型实参");
        
        // 检查是否还有其他约束
        if (typeParameter.ConstraintTypes.Length > 0)
        {
            Console.WriteLine("\n额外的类型约束:");
            foreach (var constraint in typeParameter.ConstraintTypes)
            {
                Console.WriteLine($"  - {constraint.ToDisplayString()}");
            }
        }
    }
}

值类型约束(struct)

csharp
/// <summary>
/// 分析值类型约束
/// </summary>
public void AnalyzeValueTypeConstraint(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.HasValueTypeConstraint)
    {
        Console.WriteLine($"类型参数 {typeParameter.Name} 有 struct 约束");
        Console.WriteLine("只能使用值类型作为类型实参");
        
        // struct 约束隐含 new() 约束
        if (typeParameter.HasConstructorConstraint)
        {
            Console.WriteLine("注意: struct 约束隐含 new() 约束");
        }
    }
}

构造函数约束(new())

csharp
/// <summary>
/// 分析构造函数约束
/// </summary>
public void AnalyzeConstructorConstraint(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.HasConstructorConstraint)
    {
        Console.WriteLine($"类型参数 {typeParameter.Name} 有 new() 约束");
        Console.WriteLine("类型实参必须有公共无参构造函数");
        
        // 检查是否与 struct 约束一起使用
        if (typeParameter.HasValueTypeConstraint)
        {
            Console.WriteLine("注意: struct 约束已经隐含 new() 约束");
        }
    }
}

类型约束

csharp
/// <summary>
/// 分析类型约束
/// </summary>
public void AnalyzeTypeConstraints(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.ConstraintTypes.Length == 0)
    {
        Console.WriteLine($"类型参数 {typeParameter.Name} 没有类型约束");
        return;
    }
    
    Console.WriteLine($"类型参数 {typeParameter.Name} 的类型约束:");
    
    foreach (var constraint in typeParameter.ConstraintTypes)
    {
        Console.WriteLine($"\n约束类型: {constraint.ToDisplayString()}");
        Console.WriteLine($"  类型种类: {constraint.TypeKind}");
        
        if (constraint.TypeKind == TypeKind.Interface)
        {
            Console.WriteLine("  这是接口约束");
        }
        else if (constraint.TypeKind == TypeKind.Class)
        {
            Console.WriteLine("  这是基类约束");
        }
        else if (constraint.TypeKind == TypeKind.TypeParameter)
        {
            Console.WriteLine("  这是类型参数约束(裸类型约束)");
        }
    }
}

非托管类型约束(unmanaged)

csharp
/// <summary>
/// 分析非托管类型约束
/// </summary>
public void AnalyzeUnmanagedConstraint(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.HasUnmanagedTypeConstraint)
    {
        Console.WriteLine($"类型参数 {typeParameter.Name} 有 unmanaged 约束");
        Console.WriteLine("只能使用非托管类型作为类型实参");
        Console.WriteLine("非托管类型包括:");
        Console.WriteLine("  - 基本类型(int, float, bool 等)");
        Console.WriteLine("  - 枚举类型");
        Console.WriteLine("  - 指针类型");
        Console.WriteLine("  - 只包含非托管类型字段的结构");
    }
}

notnull 约束

csharp
/// <summary>
/// 分析 notnull 约束
/// </summary>
public void AnalyzeNotNullConstraint(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.HasNotNullConstraint)
    {
        Console.WriteLine($"类型参数 {typeParameter.Name} 有 notnull 约束");
        Console.WriteLine("类型实参不能是可空引用类型");
    }
}
点击查看完整示例:约束验证器
csharp
/// <summary>
/// 类型参数约束验证器
/// </summary>
public class TypeParameterConstraintValidator
{
    /// <summary>
    /// 验证类型是否满足类型参数的所有约束
    /// </summary>
    public bool ValidateConstraints(
        ITypeParameterSymbol typeParameter,
        ITypeSymbol typeArgument)
    {
        Console.WriteLine($"验证类型 {typeArgument.ToDisplayString()} 是否满足约束");
        Console.WriteLine();
        
        bool isValid = true;
        
        // 1. 检查引用类型约束
        if (typeParameter.HasReferenceTypeConstraint)
        {
            if (!typeArgument.IsReferenceType)
            {
                Console.WriteLine("❌ 不满足 class 约束:类型不是引用类型");
                isValid = false;
            }
            else
            {
                Console.WriteLine("✓ 满足 class 约束");
            }
        }
        
        // 2. 检查值类型约束
        if (typeParameter.HasValueTypeConstraint)
        {
            if (!typeArgument.IsValueType)
            {
                Console.WriteLine("❌ 不满足 struct 约束:类型不是值类型");
                isValid = false;
            }
            else
            {
                Console.WriteLine("✓ 满足 struct 约束");
            }
        }
        
        // 3. 检查构造函数约束
        if (typeParameter.HasConstructorConstraint)
        {
            bool hasPublicParameterlessConstructor = HasPublicParameterlessConstructor(typeArgument);
            if (!hasPublicParameterlessConstructor)
            {
                Console.WriteLine("❌ 不满足 new() 约束:类型没有公共无参构造函数");
                isValid = false;
            }
            else
            {
                Console.WriteLine("✓ 满足 new() 约束");
            }
        }
        
        // 4. 检查类型约束
        foreach (var constraint in typeParameter.ConstraintTypes)
        {
            bool satisfiesConstraint = SatisfiesTypeConstraint(typeArgument, constraint);
            if (!satisfiesConstraint)
            {
                Console.WriteLine($"❌ 不满足类型约束: {constraint.ToDisplayString()}");
                isValid = false;
            }
            else
            {
                Console.WriteLine($"✓ 满足类型约束: {constraint.ToDisplayString()}");
            }
        }
        
        Console.WriteLine();
        Console.WriteLine(isValid ? "验证通过 ✓" : "验证失败 ❌");
        
        return isValid;
    }
    
    private bool HasPublicParameterlessConstructor(ITypeSymbol type)
    {
        if (type is not INamedTypeSymbol namedType)
            return false;
        
        // 值类型总是有默认构造函数
        if (namedType.IsValueType)
            return true;
        
        // 检查是否有公共无参构造函数
        var constructors = namedType.InstanceConstructors;
        return constructors.Any(c => 
            c.DeclaredAccessibility == Accessibility.Public && 
            c.Parameters.Length == 0);
    }
    
    private bool SatisfiesTypeConstraint(ITypeSymbol type, ITypeSymbol constraint)
    {
        // 检查是否派生自约束类型或实现了约束接口
        if (constraint.TypeKind == TypeKind.Interface)
        {
            if (type is INamedTypeSymbol namedType)
            {
                return namedType.AllInterfaces.Contains(
                    constraint as INamedTypeSymbol,
                    SymbolEqualityComparer.Default);
            }
        }
        else if (constraint.TypeKind == TypeKind.Class)
        {
            var current = type;
            while (current != null)
            {
                if (SymbolEqualityComparer.Default.Equals(current, constraint))
                    return true;
                
                if (current is INamedTypeSymbol namedCurrent)
                    current = namedCurrent.BaseType;
                else
                    break;
            }
        }
        
        return false;
    }
}

关键要点:

  • 引用类型约束和值类型约束是互斥的
  • struct 约束隐含 new() 约束
  • 类型约束可以是基类、接口或其他类型参数
  • 可以同时有多个类型约束
  • 约束的顺序有要求:class/struct → 类型约束 → new()

🟡 泛型方法和类型

类型参数可以声明在类型或方法上。

类型的类型参数

csharp
/// <summary>
/// 分析类型的类型参数
/// </summary>
public void AnalyzeTypeTypeParameters(INamedTypeSymbol typeSymbol)
{
    if (!typeSymbol.IsGenericType)
    {
        Console.WriteLine("这不是泛型类型");
        return;
    }
    
    Console.WriteLine($"泛型类型: {typeSymbol.ToDisplayString()}");
    Console.WriteLine($"类型参数数量: {typeSymbol.TypeParameters.Length}");
    Console.WriteLine();
    
    for (int i = 0; i < typeSymbol.TypeParameters.Length; i++)
    {
        var typeParam = typeSymbol.TypeParameters[i];
        Console.WriteLine($"类型参数 {i}: {typeParam.Name}");
        
        // 显示约束
        var constraints = GetConstraintDescription(typeParam);
        if (!string.IsNullOrEmpty(constraints))
        {
            Console.WriteLine($"  约束: {constraints}");
        }
        
        Console.WriteLine();
    }
}

方法的类型参数

csharp
/// <summary>
/// 分析方法的类型参数
/// </summary>
public void AnalyzeMethodTypeParameters(IMethodSymbol methodSymbol)
{
    if (!methodSymbol.IsGenericMethod)
    {
        Console.WriteLine("这不是泛型方法");
        return;
    }
    
    Console.WriteLine($"泛型方法: {methodSymbol.ToDisplayString()}");
    Console.WriteLine($"类型参数数量: {methodSymbol.TypeParameters.Length}");
    Console.WriteLine();
    
    for (int i = 0; i < methodSymbol.TypeParameters.Length; i++)
    {
        var typeParam = methodSymbol.TypeParameters[i];
        Console.WriteLine($"类型参数 {i}: {typeParam.Name}");
        
        // 显示约束
        var constraints = GetConstraintDescription(typeParam);
        if (!string.IsNullOrEmpty(constraints))
        {
            Console.WriteLine($"  约束: {constraints}");
        }
        
        Console.WriteLine();
    }
}

private string GetConstraintDescription(ITypeParameterSymbol typeParam)
{
    var constraints = new List<string>();
    
    if (typeParam.HasReferenceTypeConstraint)
        constraints.Add("class");
    if (typeParam.HasValueTypeConstraint)
        constraints.Add("struct");
    if (typeParam.HasUnmanagedTypeConstraint)
        constraints.Add("unmanaged");
    if (typeParam.HasNotNullConstraint)
        constraints.Add("notnull");
    
    foreach (var constraint in typeParam.ConstraintTypes)
    {
        constraints.Add(constraint.ToDisplayString());
    }
    
    if (typeParam.HasConstructorConstraint)
        constraints.Add("new()");
    
    return string.Join(", ", constraints);
}

区分类型参数和类型实参

csharp
/// <summary>
/// 区分类型参数和类型实参
/// </summary>
public void DistinguishTypeParametersAndArguments(INamedTypeSymbol typeSymbol)
{
    Console.WriteLine($"类型: {typeSymbol.ToDisplayString()}");
    Console.WriteLine();
    
    // 类型参数(来自原始定义)
    var originalDef = typeSymbol.OriginalDefinition;
    Console.WriteLine("类型参数(Type Parameters):");
    foreach (var typeParam in originalDef.TypeParameters)
    {
        Console.WriteLine($"  - {typeParam.Name} (类型参数符号)");
    }
    Console.WriteLine();
    
    // 类型实参(当前类型的实际类型)
    if (!typeSymbol.IsUnboundGenericType)
    {
        Console.WriteLine("类型实参(Type Arguments):");
        foreach (var typeArg in typeSymbol.TypeArguments)
        {
            Console.WriteLine($"  - {typeArg.ToDisplayString()} ({typeArg.TypeKind})");
        }
    }
}

裸类型约束

csharp
/// <summary>
/// 分析裸类型约束(类型参数约束另一个类型参数)
/// </summary>
public void AnalyzeNakedTypeConstraint(ITypeParameterSymbol typeParameter)
{
    var nakedConstraints = typeParameter.ConstraintTypes
        .Where(c => c.TypeKind == TypeKind.TypeParameter)
        .Cast<ITypeParameterSymbol>()
        .ToList();
    
    if (nakedConstraints.Count == 0)
    {
        Console.WriteLine("没有裸类型约束");
        return;
    }
    
    Console.WriteLine($"类型参数 {typeParameter.Name} 的裸类型约束:");
    foreach (var constraint in nakedConstraints)
    {
        Console.WriteLine($"  - {constraint.Name}");
        Console.WriteLine($"    序号: {constraint.Ordinal}");
    }
}

// 示例:class MyClass<T, U> where U : T
// U 有裸类型约束 T

🟡 协变和逆变

协变和逆变允许泛型类型参数在继承层次中更灵活地使用。

协变(out)

csharp
/// <summary>
/// 分析协变类型参数
/// </summary>
public void AnalyzeCovariance(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.Variance != VarianceKind.Out)
    {
        Console.WriteLine("这不是协变类型参数");
        return;
    }
    
    Console.WriteLine($"协变类型参数: {typeParameter.Name}");
    Console.WriteLine("使用 out 修饰符");
    Console.WriteLine();
    
    Console.WriteLine("协变规则:");
    Console.WriteLine("  - 只能用作返回类型");
    Console.WriteLine("  - 不能用作方法参数类型");
    Console.WriteLine("  - 允许向上转型");
    Console.WriteLine();
    
    Console.WriteLine("示例:");
    Console.WriteLine("  IEnumerable<string> strings = ...;");
    Console.WriteLine("  IEnumerable<object> objects = strings; // 合法");
}

逆变(in)

csharp
/// <summary>
/// 分析逆变类型参数
/// </summary>
public void AnalyzeContravariance(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.Variance != VarianceKind.In)
    {
        Console.WriteLine("这不是逆变类型参数");
        return;
    }
    
    Console.WriteLine($"逆变类型参数: {typeParameter.Name}");
    Console.WriteLine("使用 in 修饰符");
    Console.WriteLine();
    
    Console.WriteLine("逆变规则:");
    Console.WriteLine("  - 只能用作方法参数类型");
    Console.WriteLine("  - 不能用作返回类型");
    Console.WriteLine("  - 允许向下转型");
    Console.WriteLine();
    
    Console.WriteLine("示例:");
    Console.WriteLine("  IComparer<object> objectComparer = ...;");
    Console.WriteLine("  IComparer<string> stringComparer = objectComparer; // 合法");
}

不变(invariant)

csharp
/// <summary>
/// 分析不变类型参数
/// </summary>
public void AnalyzeInvariance(ITypeParameterSymbol typeParameter)
{
    if (typeParameter.Variance != VarianceKind.None)
    {
        Console.WriteLine("这不是不变类型参数");
        return;
    }
    
    Console.WriteLine($"不变类型参数: {typeParameter.Name}");
    Console.WriteLine("没有 in 或 out 修饰符");
    Console.WriteLine();
    
    Console.WriteLine("不变规则:");
    Console.WriteLine("  - 可以用作返回类型");
    Console.WriteLine("  - 可以用作方法参数类型");
    Console.WriteLine("  - 不允许类型转换");
    Console.WriteLine();
    
    Console.WriteLine("示例:");
    Console.WriteLine("  List<string> strings = ...;");
    Console.WriteLine("  List<object> objects = strings; // 非法");
}
点击查看完整示例:协变逆变分析器
csharp
/// <summary>
/// 协变逆变分析器
/// </summary>
public class VarianceAnalyzer
{
    public void AnalyzeTypeVariance(INamedTypeSymbol typeSymbol)
    {
        if (!typeSymbol.IsGenericType)
        {
            Console.WriteLine("这不是泛型类型");
            return;
        }
        
        Console.WriteLine($"分析类型: {typeSymbol.ToDisplayString()}");
        Console.WriteLine($"类型种类: {typeSymbol.TypeKind}");
        Console.WriteLine();
        
        // 只有接口和委托可以有协变逆变
        if (typeSymbol.TypeKind != TypeKind.Interface && 
            typeSymbol.TypeKind != TypeKind.Delegate)
        {
            Console.WriteLine("注意: 只有接口和委托可以有协变逆变类型参数");
            Console.WriteLine();
        }
        
        // 分析每个类型参数
        Console.WriteLine("类型参数:");
        foreach (var typeParam in typeSymbol.TypeParameters)
        {
            Console.WriteLine($"\n  {typeParam.Name}:");
            Console.WriteLine($"    序号: {typeParam.Ordinal}");
            Console.WriteLine($"    协变/逆变: {GetVarianceDescription(typeParam.Variance)}");
            
            // 显示约束
            if (typeParam.ConstraintTypes.Length > 0 ||
                typeParam.HasReferenceTypeConstraint ||
                typeParam.HasValueTypeConstraint ||
                typeParam.HasConstructorConstraint)
            {
                Console.WriteLine($"    约束: {GetConstraintDescription(typeParam)}");
            }
            
            // 检查协变逆变的使用
            if (typeParam.Variance != VarianceKind.None)
            {
                AnalyzeVarianceUsage(typeSymbol, typeParam);
            }
        }
    }
    
    private string GetVarianceDescription(VarianceKind variance)
    {
        return variance switch
        {
            VarianceKind.Out => "协变 (out)",
            VarianceKind.In => "逆变 (in)",
            VarianceKind.None => "不变",
            _ => "未知"
        };
    }
    
    private void AnalyzeVarianceUsage(
        INamedTypeSymbol typeSymbol,
        ITypeParameterSymbol typeParam)
    {
        Console.WriteLine($"    使用位置:");
        
        // 分析方法中的使用
        var methods = typeSymbol.GetMembers().OfType<IMethodSymbol>();
        foreach (var method in methods)
        {
            // 检查返回类型
            if (UsesTypeParameter(method.ReturnType, typeParam))
            {
                Console.WriteLine($"      - 返回类型: {method.Name}");
            }
            
            // 检查参数类型
            foreach (var param in method.Parameters)
            {
                if (UsesTypeParameter(param.Type, typeParam))
                {
                    Console.WriteLine($"      - 参数类型: {method.Name}({param.Name})");
                }
            }
        }
    }
    
    private bool UsesTypeParameter(ITypeSymbol type, ITypeParameterSymbol typeParam)
    {
        if (SymbolEqualityComparer.Default.Equals(type, typeParam))
            return true;
        
        if (type is INamedTypeSymbol namedType && namedType.IsGenericType)
        {
            return namedType.TypeArguments.Any(arg => UsesTypeParameter(arg, typeParam));
        }
        
        return false;
    }
    
    private string GetConstraintDescription(ITypeParameterSymbol typeParam)
    {
        var constraints = new List<string>();
        
        if (typeParam.HasReferenceTypeConstraint)
            constraints.Add("class");
        if (typeParam.HasValueTypeConstraint)
            constraints.Add("struct");
        
        foreach (var constraint in typeParam.ConstraintTypes)
        {
            constraints.Add(constraint.ToDisplayString());
        }
        
        if (typeParam.HasConstructorConstraint)
            constraints.Add("new()");
        
        return string.Join(", ", constraints);
    }
}

关键要点:

  • 协变(out):类型参数只能用作输出(返回类型)
  • 逆变(in):类型参数只能用作输入(参数类型)
  • 只有接口和委托可以有协变逆变类型参数
  • 协变允许向上转型,逆变允许向下转型
  • 不变类型参数不允许任何转换

注意

协变和逆变只能用于接口和委托类型,不能用于类和结构。


🔗 相关 API


📚 下一步

基于 MIT 许可发布