Skip to content

属性符号(IPropertySymbol)

📋 文档信息

难度: 🟡 中级
预计阅读时间: 25 分钟
前置知识: 语法树基础、符号系统、语义模型基础
适合人群: 中级开发者


📋 快速导航

章节难度阅读时间链接
概览🟢2 分钟查看
核心属性🟡5 分钟查看
属性类型详解🟡6 分钟查看
访问器详解🟡4 分钟查看
完整示例🟡4 分钟查看
真实使用场景🔴6 分钟查看
最佳实践🟡4 分钟查看
反模式和常见错误🟡4 分钟查看

🎯 概览

IPropertySymbol 表示 C# 中的属性,包括自动属性、计算属性、只读属性、只写属性和索引器。属性是 C# 中封装字段访问的重要机制。

本文档涵盖:

  • 属性符号的核心属性和方法
  • 不同类型的属性(自动属性、计算属性、索引器等)
  • 访问器(get/set)的分析
  • 属性的可访问性和修饰符
  • 实际应用场景和最佳实践

典型应用场景:

  • DTO 映射代码生成
  • 数据绑定分析
  • 序列化器实现
  • 属性验证代码生成

核心属性

IPropertySymbol 提供了丰富的属性来描述 C# 属性的各个方面:

csharp
IPropertySymbol propertySymbol;

// ============================================================
// 基本信息
// ============================================================

// 属性名称
string name = propertySymbol.Name;

// 属性类型
ITypeSymbol type = propertySymbol.Type;

// 是否是自动属性
bool isAutoProperty = propertySymbol.IsAutoProperty;

// 是否是索引器
bool isIndexer = propertySymbol.IsIndexer;

// 是否是只读属性
bool isReadOnly = propertySymbol.IsReadOnly;

// 是否是只写属性
bool isWriteOnly = propertySymbol.IsWriteOnly;

// 是否是抽象属性
bool isAbstract = propertySymbol.IsAbstract;

// 是否是虚拟属性
bool isVirtual = propertySymbol.IsVirtual;

// 是否是重写属性
bool isOverride = propertySymbol.IsOverride;

// 是否是密封属性
bool isSealed = propertySymbol.IsSealed;

// 是否是静态属性
bool isStatic = propertySymbol.IsStatic;

// 是否是必需属性 (C# 11+)
bool isRequired = propertySymbol.IsRequired;

// 是否是 extern 属性
bool isExtern = propertySymbol.IsExtern;

// ============================================================
// 访问器
// ============================================================

// Get 访问器
IMethodSymbol? getMethod = propertySymbol.GetMethod;

// Set 访问器
IMethodSymbol? setMethod = propertySymbol.SetMethod;

// 检查是否有 Get 访问器
bool hasGetter = getMethod != null;

// 检查是否有 Set 访问器
bool hasSetter = setMethod != null;

// Get 访问器的访问级别
Accessibility? getterAccessibility = getMethod?.DeclaredAccessibility;

// Set 访问器的访问级别
Accessibility? setterAccessibility = setMethod?.DeclaredAccessibility;

// ============================================================
// 索引器相关
// ============================================================

// 索引器参数(仅对索引器有效)
ImmutableArray<IParameterSymbol> parameters = propertySymbol.Parameters;

// 索引器参数数量
int parameterCount = parameters.Length;

// ============================================================
// 显式接口实现
// ============================================================

// 显式实现的接口属性
ImmutableArray<IPropertySymbol> explicitInterfaceImplementations = 
    propertySymbol.ExplicitInterfaceImplementations;

// 是否是显式接口实现
bool isExplicitInterfaceImplementation = 
    explicitInterfaceImplementations.Length > 0;

// ============================================================
// 重写和隐藏
// ============================================================

// 被重写的属性
IPropertySymbol? overriddenProperty = propertySymbol.OverriddenProperty;

// 是否隐藏基类成员
bool isHiddenBySignature = propertySymbol.IsHiddenBySignature;

// ============================================================
// 可空性 (C# 8+)
// ============================================================

// 属性类型的可空注解
NullableAnnotation nullableAnnotation = propertySymbol.Type.NullableAnnotation;

// 是否是可空引用类型
bool isNullableReferenceType = 
    nullableAnnotation == NullableAnnotation.Annotated;

// ============================================================
// 其他属性
// ============================================================

// 包含类型
INamedTypeSymbol containingType = propertySymbol.ContainingType;

// 声明的访问级别
Accessibility accessibility = propertySymbol.DeclaredAccessibility;

// 是否是 partial 属性 (C# 13+)
bool isPartialDefinition = propertySymbol.IsPartialDefinition;

属性类型详解

自动属性 (Auto-Property)

自动属性是编译器自动生成后备字段的属性。

csharp
// 自动属性示例
public class Person
{
    // 自动属性
    public string Name { get; set; }
    
    // 只读自动属性
    public int Id { get; }
    
    // Init-only 自动属性 (C# 9+)
    public DateTime CreatedAt { get; init; }
}

// 检测自动属性
public bool IsAutoProperty(IPropertySymbol property)
{
    return property.IsAutoProperty;
}

计算属性 (Computed Property)

计算属性具有显式的 getter 和/或 setter 实现。

csharp
// 计算属性示例
public class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }
    
    // 计算属性
    public double Area => Width * Height;
    
    // 带完整实现的计算属性
    private double _diagonal;
    public double Diagonal
    {
        get => _diagonal;
        set
        {
            if (value < 0)
                throw new ArgumentException("Diagonal cannot be negative");
            _diagonal = value;
        }
    }
}

// 检测计算属性
public bool IsComputedProperty(IPropertySymbol property)
{
    return !property.IsAutoProperty;
}

只读属性和只写属性

csharp
// 只读属性示例
public class Configuration
{
    // 只读属性(只有 getter)
    public string AppName { get; } = "MyApp";
    
    // 表达式体只读属性
    public string Version => "1.0.0";
}

// 只写属性示例(罕见)
public class Logger
{
    private string _message;
    
    // 只写属性(只有 setter)
    public string Message
    {
        set => _message = value;
    }
}

// 检测只读/只写属性
public void AnalyzePropertyAccessibility(IPropertySymbol property)
{
    if (property.IsReadOnly)
    {
        Console.WriteLine($"{property.Name} 是只读属性");
    }
    
    if (property.IsWriteOnly)
    {
        Console.WriteLine($"{property.Name} 是只写属性");
    }
    
    // 检查访问器
    bool hasGetter = property.GetMethod != null;
    bool hasSetter = property.SetMethod != null;
    
    Console.WriteLine($"Has getter: {hasGetter}, Has setter: {hasSetter}");
}

索引器 (Indexer)

索引器是特殊的属性,允许使用索引访问对象。

csharp
// 索引器示例
public class DataCollection
{
    private Dictionary<string, object> _data = new();
    
    // 字符串索引器
    public object this[string key]
    {
        get => _data[key];
        set => _data[key] = value;
    }
    
    // 整数索引器
    private List<string> _items = new();
    public string this[int index]
    {
        get => _items[index];
        set => _items[index] = value;
    }
    
    // 多参数索引器
    private int[,] _matrix = new int[10, 10];
    public int this[int row, int col]
    {
        get => _matrix[row, col];
        set => _matrix[row, col] = value;
    }
}

// 检测和分析索引器
public void AnalyzeIndexer(IPropertySymbol property)
{
    if (!property.IsIndexer)
    {
        Console.WriteLine($"{property.Name} 不是索引器");
        return;
    }
    
    Console.WriteLine("索引器分析:");
    Console.WriteLine($"  参数数量: {property.Parameters.Length}");
    
    foreach (var param in property.Parameters)
    {
        Console.WriteLine($"  参数: {param.Type.ToDisplayString()} {param.Name}");
    }
    
    Console.WriteLine($"  返回类型: {property.Type.ToDisplayString()}");
}

访问器详解

Get 和 Set 访问器

csharp
/// <summary>
/// 分析属性的访问器
/// </summary>
public class PropertyAccessorAnalyzer
{
    public void AnalyzeAccessors(IPropertySymbol property)
    {
        Console.WriteLine($"属性: {property.Name}");
        
        // 分析 Get 访问器
        if (property.GetMethod != null)
        {
            var getter = property.GetMethod;
            Console.WriteLine($"  Get 访问器:");
            Console.WriteLine($"    访问级别: {getter.DeclaredAccessibility}");
            Console.WriteLine($"    是否是抽象: {getter.IsAbstract}");
            Console.WriteLine($"    是否是虚拟: {getter.IsVirtual}");
            Console.WriteLine($"    是否是重写: {getter.IsOverride}");
        }
        else
        {
            Console.WriteLine($"  没有 Get 访问器");
        }
        
        // 分析 Set 访问器
        if (property.SetMethod != null)
        {
            var setter = property.SetMethod;
            Console.WriteLine($"  Set 访问器:");
            Console.WriteLine($"    访问级别: {setter.DeclaredAccessibility}");
            Console.WriteLine($"    是否是抽象: {setter.IsAbstract}");
            Console.WriteLine($"    是否是虚拟: {setter.IsVirtual}");
            Console.WriteLine($"    是否是重写: {setter.IsOverride}");
            
            // 检查是否是 init 访问器 (C# 9+)
            bool isInitOnly = setter.IsInitOnly;
            Console.WriteLine($"    是否是 init-only: {isInitOnly}");
        }
        else
        {
            Console.WriteLine($"  没有 Set 访问器");
        }
    }
}

访问器的访问级别

csharp
/// <summary>
/// 检查属性访问器的访问级别差异
/// </summary>
public class AccessorAccessibilityChecker
{
    public void CheckAccessorAccessibility(IPropertySymbol property)
    {
        var propertyAccessibility = property.DeclaredAccessibility;
        
        Console.WriteLine($"属性 {property.Name} 的访问级别: {propertyAccessibility}");
        
        // 检查 getter 的访问级别
        if (property.GetMethod != null)
        {
            var getterAccessibility = property.GetMethod.DeclaredAccessibility;
            
            if (getterAccessibility != propertyAccessibility)
            {
                Console.WriteLine($"  Get 访问器有不同的访问级别: {getterAccessibility}");
            }
        }
        
        // 检查 setter 的访问级别
        if (property.SetMethod != null)
        {
            var setterAccessibility = property.SetMethod.DeclaredAccessibility;
            
            if (setterAccessibility != propertyAccessibility)
            {
                Console.WriteLine($"  Set 访问器有不同的访问级别: {setterAccessibility}");
            }
        }
    }
    
    /// <summary>
    /// 示例:查找具有私有 setter 的公共属性
    /// </summary>
    public IEnumerable<IPropertySymbol> FindPublicPropertiesWithPrivateSetter(
        INamedTypeSymbol type)
    {
        return type.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => 
                p.DeclaredAccessibility == Accessibility.Public &&
                p.SetMethod != null &&
                p.SetMethod.DeclaredAccessibility == Accessibility.Private);
    }
}

完整示例:属性分析器

csharp
/// <summary>
/// 完整的属性分析器,展示如何全面分析属性符号
/// </summary>
public class ComprehensivePropertyAnalyzer
{
    public PropertyAnalysisResult AnalyzeProperty(IPropertySymbol property)
    {
        var result = new PropertyAnalysisResult
        {
            Name = property.Name,
            Type = property.Type.ToDisplayString(),
            ContainingType = property.ContainingType.ToDisplayString(),
            Accessibility = property.DeclaredAccessibility.ToString(),
            
            // 属性特性
            IsAutoProperty = property.IsAutoProperty,
            IsIndexer = property.IsIndexer,
            IsReadOnly = property.IsReadOnly,
            IsWriteOnly = property.IsWriteOnly,
            IsAbstract = property.IsAbstract,
            IsVirtual = property.IsVirtual,
            IsOverride = property.IsOverride,
            IsSealed = property.IsSealed,
            IsStatic = property.IsStatic,
            IsRequired = property.IsRequired,
            IsExtern = property.IsExtern
        };
        
        // 分析访问器
        if (property.GetMethod != null)
        {
            result.HasGetter = true;
            result.GetterAccessibility = 
                property.GetMethod.DeclaredAccessibility.ToString();
        }
        
        if (property.SetMethod != null)
        {
            result.HasSetter = true;
            result.SetterAccessibility = 
                property.SetMethod.DeclaredAccessibility.ToString();
            result.IsInitOnly = property.SetMethod.IsInitOnly;
        }
        
        // 分析索引器参数
        if (property.IsIndexer)
        {
            result.IndexerParameters = property.Parameters
                .Select(p => new ParameterInfo
                {
                    Name = p.Name,
                    Type = p.Type.ToDisplayString(),
                    RefKind = p.RefKind.ToString()
                })
                .ToList();
        }
        
        // 分析重写
        if (property.OverriddenProperty != null)
        {
            result.OverriddenProperty = 
                property.OverriddenProperty.ToDisplayString();
        }
        
        // 分析显式接口实现
        if (property.ExplicitInterfaceImplementations.Length > 0)
        {
            result.ExplicitInterfaceImplementations = 
                property.ExplicitInterfaceImplementations
                    .Select(p => p.ToDisplayString())
                    .ToList();
        }
        
        // 分析可空性
        result.NullableAnnotation = 
            property.Type.NullableAnnotation.ToString();
        
        return result;
    }
}

public class PropertyAnalysisResult
{
    public string Name { get; set; }
    public string Type { get; set; }
    public string ContainingType { get; set; }
    public string Accessibility { get; set; }
    
    // 属性特性
    public bool IsAutoProperty { get; set; }
    public bool IsIndexer { get; set; }
    public bool IsReadOnly { get; set; }
    public bool IsWriteOnly { get; set; }
    public bool IsAbstract { get; set; }
    public bool IsVirtual { get; set; }
    public bool IsOverride { get; set; }
    public bool IsSealed { get; set; }
    public bool IsStatic { get; set; }
    public bool IsRequired { get; set; }
    public bool IsExtern { get; set; }
    
    // 访问器信息
    public bool HasGetter { get; set; }
    public bool HasSetter { get; set; }
    public string? GetterAccessibility { get; set; }
    public string? SetterAccessibility { get; set; }
    public bool IsInitOnly { get; set; }
    
    // 索引器信息
    public List<ParameterInfo>? IndexerParameters { get; set; }
    
    // 重写和接口实现
    public string? OverriddenProperty { get; set; }
    public List<string>? ExplicitInterfaceImplementations { get; set; }
    
    // 可空性
    public string NullableAnnotation { get; set; }
}

public class ParameterInfo
{
    public string Name { get; set; }
    public string Type { get; set; }
    public string RefKind { get; set; }
}

真实使用场景

场景 1:生成属性的序列化代码

csharp
/// <summary>
/// 为可序列化的属性生成 JSON 序列化代码
/// </summary>
public class PropertySerializationGenerator
{
    public string GenerateSerializationCode(INamedTypeSymbol type)
    {
        var sb = new StringBuilder();
        sb.AppendLine($"public class {type.Name}Serializer");
        sb.AppendLine("{");
        sb.AppendLine($"    public string Serialize({type.Name} obj)");
        sb.AppendLine("    {");
        sb.AppendLine("        var json = new StringBuilder();");
        sb.AppendLine("        json.Append(\"{\");");
        
        var properties = type.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => ShouldSerialize(p))
            .ToList();
        
        for (int i = 0; i < properties.Count; i++)
        {
            var property = properties[i];
            var comma = i < properties.Count - 1 ? "," : "";
            
            sb.AppendLine($"        json.Append(\"\\\"{property.Name}\\\":\");");
            sb.AppendLine($"        json.Append(SerializeValue(obj.{property.Name}));");
            sb.AppendLine($"        json.Append(\"{comma}\");");
        }
        
        sb.AppendLine("        json.Append(\"}\");");
        sb.AppendLine("        return json.ToString();");
        sb.AppendLine("    }");
        sb.AppendLine("}");
        
        return sb.ToString();
    }
    
    private bool ShouldSerialize(IPropertySymbol property)
    {
        // 只序列化公共的、可读的、非索引器的属性
        return property.DeclaredAccessibility == Accessibility.Public &&
               property.GetMethod != null &&
               !property.IsIndexer &&
               !property.IsStatic;
    }
}

场景 2:验证属性的可空性

csharp
/// <summary>
/// 验证属性的可空性配置是否正确
/// </summary>
public class PropertyNullabilityValidator
{
    public List<string> ValidateNullability(INamedTypeSymbol type)
    {
        var issues = new List<string>();
        
        foreach (var property in type.GetMembers().OfType<IPropertySymbol>())
        {
            // 检查引用类型属性的可空性
            if (property.Type.IsReferenceType)
            {
                var nullableAnnotation = property.Type.NullableAnnotation;
                
                // 如果属性是必需的,但标记为可空,发出警告
                if (property.IsRequired && 
                    nullableAnnotation == NullableAnnotation.Annotated)
                {
                    issues.Add(
                        $"属性 {property.Name} 标记为 required," +
                        $"但类型是可空的 ({property.Type.ToDisplayString()})");
                }
                
                // 如果属性没有 setter 且没有初始化器,但不是可空的
                if (property.SetMethod == null && 
                    !property.IsRequired &&
                    nullableAnnotation == NullableAnnotation.NotAnnotated)
                {
                    issues.Add(
                        $"只读属性 {property.Name} 可能未初始化," +
                        $"应该标记为可空或添加初始化器");
                }
            }
            
            // 检查 init-only 属性
            if (property.SetMethod?.IsInitOnly == true)
            {
                // Init-only 属性应该考虑是否需要 required 修饰符
                if (!property.IsRequired && 
                    property.Type.IsReferenceType &&
                    property.Type.NullableAnnotation == NullableAnnotation.NotAnnotated)
                {
                    issues.Add(
                        $"Init-only 属性 {property.Name} 可能需要 required 修饰符");
                }
            }
        }
        
        return issues;
    }
}

场景 3:分析属性的访问模式

csharp
/// <summary>
/// 分析类型中属性的访问模式,用于优化或重构建议
/// </summary>
public class PropertyAccessPatternAnalyzer
{
    public PropertyAccessReport AnalyzeAccessPatterns(INamedTypeSymbol type)
    {
        var report = new PropertyAccessReport();
        
        foreach (var property in type.GetMembers().OfType<IPropertySymbol>())
        {
            // 分析不同的访问模式
            
            // 1. 公共 getter,私有 setter(常见的封装模式)
            if (property.GetMethod?.DeclaredAccessibility == Accessibility.Public &&
                property.SetMethod?.DeclaredAccessibility == Accessibility.Private)
            {
                report.PublicGetPrivateSet.Add(property.Name);
            }
            
            // 2. 只读属性(不可变模式)
            if (property.IsReadOnly)
            {
                report.ReadOnlyProperties.Add(property.Name);
            }
            
            // 3. Init-only 属性(C# 9+ 不可变模式)
            if (property.SetMethod?.IsInitOnly == true)
            {
                report.InitOnlyProperties.Add(property.Name);
            }
            
            // 4. Required 属性(C# 11+ 必需初始化)
            if (property.IsRequired)
            {
                report.RequiredProperties.Add(property.Name);
            }
            
            // 5. 自动属性(简化模式)
            if (property.IsAutoProperty)
            {
                report.AutoProperties.Add(property.Name);
            }
            
            // 6. 计算属性(逻辑封装)
            if (!property.IsAutoProperty && property.GetMethod != null)
            {
                report.ComputedProperties.Add(property.Name);
            }
        }
        
        return report;
    }
}

public class PropertyAccessReport
{
    public List<string> PublicGetPrivateSet { get; set; } = new();
    public List<string> ReadOnlyProperties { get; set; } = new();
    public List<string> InitOnlyProperties { get; set; } = new();
    public List<string> RequiredProperties { get; set; } = new();
    public List<string> AutoProperties { get; set; } = new();
    public List<string> ComputedProperties { get; set; } = new();
}

💡 最佳实践

  1. 检查访问器是否存在

    csharp
    // ✅ 正确:始终检查访问器是否为 null
    if (property.GetMethod != null)
    {
        var accessibility = property.GetMethod.DeclaredAccessibility;
    }
    
    // ❌ 错误:假设访问器总是存在
    var accessibility = property.GetMethod.DeclaredAccessibility; // 可能 NullReferenceException
  2. 使用 IsReadOnly 和 IsWriteOnly 属性

    csharp
    // ✅ 正确:使用便捷属性
    if (property.IsReadOnly)
    {
        // 处理只读属性
    }
    
    // ❌ 不推荐:手动检查访问器
    if (property.GetMethod != null && property.SetMethod == null)
    {
        // 处理只读属性
    }
  3. 区分自动属性和计算属性

    csharp
    // ✅ 正确:使用 IsAutoProperty 区分
    if (property.IsAutoProperty)
    {
        // 自动属性有编译器生成的后备字段
        // 可以通过 AssociatedSymbol 访问
    }
    else
    {
        // 计算属性有自定义实现
        // 需要分析 getter/setter 的方法体
    }
  4. 处理索引器

    csharp
    // ✅ 正确:检查 IsIndexer 属性
    if (property.IsIndexer)
    {
        // 索引器有参数
        foreach (var param in property.Parameters)
        {
            Console.WriteLine($"索引参数: {param.Type} {param.Name}");
        }
    }
  5. 检查 Init-only 属性

    csharp
    // ✅ 正确:检查 SetMethod 的 IsInitOnly 属性
    if (property.SetMethod?.IsInitOnly == true)
    {
        // 这是 init-only 属性(C# 9+)
        // 只能在对象初始化器中设置
    }
  6. 分析访问器的访问级别

    csharp
    // ✅ 正确:分别检查属性和访问器的访问级别
    var propertyAccessibility = property.DeclaredAccessibility;
    var getterAccessibility = property.GetMethod?.DeclaredAccessibility;
    var setterAccessibility = property.SetMethod?.DeclaredAccessibility;
    
    // 访问器可以有不同的访问级别
    // 例如:public string Name { get; private set; }
  7. 处理显式接口实现

    csharp
    // ✅ 正确:检查显式接口实现
    if (property.ExplicitInterfaceImplementations.Length > 0)
    {
        // 这是显式接口实现的属性
        // 名称可能包含接口名称前缀
        var interfaceProperty = property.ExplicitInterfaceImplementations[0];
    }
  8. 验证 Required 属性

    csharp
    // ✅ 正确:检查 C# 11+ 的 required 属性
    if (property.IsRequired)
    {
        // 这个属性必须在对象初始化时设置
        // 适用于确保对象创建时的完整性
    }
  9. 处理属性的可空性

    csharp
    // ✅ 正确:检查属性类型的可空注解
    var nullableAnnotation = property.Type.NullableAnnotation;
    
    if (nullableAnnotation == NullableAnnotation.Annotated)
    {
        // 这是可空引用类型属性(如 string?)
    }
    else if (nullableAnnotation == NullableAnnotation.NotAnnotated)
    {
        // 这是非空引用类型属性(如 string)
    }
  10. 分析属性的重写关系

    csharp
    // ✅ 正确:追踪属性的重写链
    var current = property;
    while (current.OverriddenProperty != null)
    {
        current = current.OverriddenProperty;
        Console.WriteLine($"重写了: {current.ToDisplayString()}");
    }
    // current 现在是最基础的属性定义

⚠️ 反模式和常见错误

反模式 1:忽略访问器的空值检查

csharp
// ❌ 错误:假设所有属性都有 getter 和 setter
public void BadExample(IPropertySymbol property)
{
    // 可能抛出 NullReferenceException
    var getterAccessibility = property.GetMethod.DeclaredAccessibility;
    var setterAccessibility = property.SetMethod.DeclaredAccessibility;
}

// ✅ 正确:始终检查访问器是否存在
public void GoodExample(IPropertySymbol property)
{
    if (property.GetMethod != null)
    {
        var getterAccessibility = property.GetMethod.DeclaredAccessibility;
    }
    
    if (property.SetMethod != null)
    {
        var setterAccessibility = property.SetMethod.DeclaredAccessibility;
    }
}

反模式 2:混淆属性和字段

csharp
// ❌ 错误:将自动属性的后备字段当作普通字段处理
public void BadExample(IPropertySymbol property)
{
    if (property.IsAutoProperty)
    {
        // 错误:尝试直接访问后备字段
        // 后备字段是编译器生成的,名称不可预测
        var backingField = property.ContainingType
            .GetMembers($"<{property.Name}>k__BackingField")
            .FirstOrDefault();
    }
}

// ✅ 正确:使用 AssociatedSymbol 访问后备字段
public void GoodExample(IPropertySymbol property)
{
    if (property.IsAutoProperty)
    {
        // 注意:并非所有自动属性都有可访问的后备字段符号
        // 这取决于编译器实现
    }
}

反模式 3:忽略索引器的参数

csharp
// ❌ 错误:假设所有属性都没有参数
public void BadExample(IPropertySymbol property)
{
    // 忽略了索引器有参数的情况
    Console.WriteLine($"属性类型: {property.Type}");
}

// ✅ 正确:检查是否是索引器并处理参数
public void GoodExample(IPropertySymbol property)
{
    if (property.IsIndexer)
    {
        Console.WriteLine($"索引器返回类型: {property.Type}");
        Console.WriteLine($"索引器参数:");
        foreach (var param in property.Parameters)
        {
            Console.WriteLine($"  {param.Type} {param.Name}");
        }
    }
    else
    {
        Console.WriteLine($"属性类型: {property.Type}");
    }
}

反模式 4:不考虑 Init-only 属性

csharp
// ❌ 错误:将 init-only 属性当作普通可写属性
public void BadExample(IPropertySymbol property)
{
    if (property.SetMethod != null)
    {
        // 假设属性可以在任何时候设置
        Console.WriteLine($"{property.Name} 是可写属性");
    }
}

// ✅ 正确:区分 set 和 init 访问器
public void GoodExample(IPropertySymbol property)
{
    if (property.SetMethod != null)
    {
        if (property.SetMethod.IsInitOnly)
        {
            Console.WriteLine($"{property.Name} 是 init-only 属性(仅在初始化时可设置)");
        }
        else
        {
            Console.WriteLine($"{property.Name} 是可写属性");
        }
    }
}

反模式 5:忽略 Required 属性

csharp
// ❌ 错误:不检查 required 修饰符
public void BadExample(INamedTypeSymbol type)
{
    var properties = type.GetMembers()
        .OfType<IPropertySymbol>()
        .Where(p => p.SetMethod != null || p.SetMethod?.IsInitOnly == true);
    
    // 没有区分 required 和可选属性
}

// ✅ 正确:考虑 required 属性
public void GoodExample(INamedTypeSymbol type)
{
    var requiredProperties = type.GetMembers()
        .OfType<IPropertySymbol>()
        .Where(p => p.IsRequired)
        .ToList();
    
    var optionalProperties = type.GetMembers()
        .OfType<IPropertySymbol>()
        .Where(p => !p.IsRequired && 
                    (p.SetMethod != null || p.SetMethod?.IsInitOnly == true))
        .ToList();
    
    Console.WriteLine($"必需属性: {requiredProperties.Count}");
    Console.WriteLine($"可选属性: {optionalProperties.Count}");
}

🔗 相关文档

本文档集

相关主题

实践指南


📚 下一步

学习完属性符号后,建议继续学习:

  1. 字段符号 - fields.md

    • 理解字段与属性的关系
    • 学习后备字段识别
    • 掌握常量值处理
  2. 参数符号 - parameters.md

    • 学习方法参数分析
    • 理解引用参数(ref/out/in)
    • 掌握可选参数和默认值
  3. 实际应用 - 常见场景

    • DTO 映射代码生成
    • 序列化器实现
    • 数据验证

最后更新: 2026-02-05
文档版本: 1.0

基于 MIT 许可发布