Skip to content

IPropertySymbol - 属性符号

深入理解 Roslyn 中的属性符号和索引器

📋 文档信息

属性
难度中级
阅读时间15 分钟
前置知识C# 属性、索引器、访问器
相关文档ISymbol 基础IMethodSymbol

🎯 学习目标

完成本文档后,你将能够:

  • ✅ 理解 IPropertySymbol 的核心属性和方法
  • ✅ 分析属性的访问器(getter/setter)
  • ✅ 处理索引器和自动属性
  • ✅ 生成属性声明代码
  • ✅ 识别属性的重写关系

📚 快速导航

主题说明
核心属性和方法IPropertySymbol 的基本 API
访问器分析分析 getter 和 setter
索引器处理处理索引器属性
完整示例实用的属性分析器
代码生成生成属性声明代码
最佳实践属性处理的最佳实践

核心属性和方法

IPropertySymbol 表示属性或索引器。

csharp
IPropertySymbol propertySymbol;

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

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

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

// 是否只读/只写
bool isReadOnly = propertySymbol.IsReadOnly;
bool isWriteOnly = propertySymbol.IsWriteOnly;

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

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

// Getter 方法
IMethodSymbol getMethod = propertySymbol.GetMethod;

// Setter 方法
IMethodSymbol setMethod = propertySymbol.SetMethod;

// ============================================================
// 索引器参数
// ============================================================

// 对于索引器
if (propertySymbol.IsIndexer)
{
    ImmutableArray<IParameterSymbol> parameters = propertySymbol.Parameters;
}

// ============================================================
// 重写
// ============================================================

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

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

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

访问器分析

属性的访问器(getter 和 setter)是 IMethodSymbol 类型。

csharp
/// <summary>
/// 分析属性的访问器
/// </summary>
public class PropertyAccessorAnalyzer
{
    public AccessorInfo AnalyzeAccessors(IPropertySymbol property)
    {
        var info = new AccessorInfo
        {
            PropertyName = property.Name,
            PropertyType = property.Type.ToDisplayString()
        };
        
        // 分析 Getter
        if (property.GetMethod != null)
        {
            info.HasGetter = true;
            info.GetterAccessibility = property.GetMethod.DeclaredAccessibility;
            info.IsGetterVirtual = property.GetMethod.IsVirtual;
            info.IsGetterAbstract = property.GetMethod.IsAbstract;
            info.IsGetterOverride = property.GetMethod.IsOverride;
            
            // 检查是否是自动实现
            info.IsGetterAutoImplemented = 
                property.GetMethod.DeclaringSyntaxReferences.Length == 0;
        }
        
        // 分析 Setter
        if (property.SetMethod != null)
        {
            info.HasSetter = true;
            info.SetterAccessibility = property.SetMethod.DeclaredAccessibility;
            info.IsSetterVirtual = property.SetMethod.IsVirtual;
            info.IsSetterAbstract = property.SetMethod.IsAbstract;
            info.IsSetterOverride = property.SetMethod.IsOverride;
            
            // 检查是否是 init-only setter (C# 9)
            info.IsInitOnly = property.SetMethod.IsInitOnly;
            
            // 检查是否是自动实现
            info.IsSetterAutoImplemented = 
                property.SetMethod.DeclaringSyntaxReferences.Length == 0;
        }
        
        return info;
    }
}

public class AccessorInfo
{
    public string PropertyName { get; set; }
    public string PropertyType { get; set; }
    
    public bool HasGetter { get; set; }
    public Accessibility? GetterAccessibility { get; set; }
    public bool IsGetterVirtual { get; set; }
    public bool IsGetterAbstract { get; set; }
    public bool IsGetterOverride { get; set; }
    public bool IsGetterAutoImplemented { get; set; }
    
    public bool HasSetter { get; set; }
    public Accessibility? SetterAccessibility { get; set; }
    public bool IsSetterVirtual { get; set; }
    public bool IsSetterAbstract { get; set; }
    public bool IsSetterOverride { get; set; }
    public bool IsInitOnly { get; set; }
    public bool IsSetterAutoImplemented { get; set; }
}

索引器处理

索引器是特殊的属性,具有参数。

csharp
/// <summary>
/// 处理索引器
/// </summary>
public class IndexerHandler
{
    /// <summary>
    /// 检查是否是索引器
    /// </summary>
    public bool IsIndexer(IPropertySymbol property)
    {
        return property.IsIndexer;
    }
    
    /// <summary>
    /// 分析索引器参数
    /// </summary>
    public List<IndexerParameter> AnalyzeIndexerParameters(IPropertySymbol indexer)
    {
        if (!indexer.IsIndexer)
            return new List<IndexerParameter>();
        
        return indexer.Parameters
            .Select(p => new IndexerParameter
            {
                Name = p.Name,
                Type = p.Type.ToDisplayString(),
                RefKind = p.RefKind,
                IsOptional = p.IsOptional,
                DefaultValue = p.HasExplicitDefaultValue 
                    ? p.ExplicitDefaultValue 
                    : null
            })
            .ToList();
    }
    
    /// <summary>
    /// 生成索引器签名
    /// </summary>
    public string GenerateIndexerSignature(IPropertySymbol indexer)
    {
        if (!indexer.IsIndexer)
            return null;
        
        var sb = new StringBuilder();
        
        // 访问修饰符
        sb.Append(GetAccessibilityString(indexer.DeclaredAccessibility));
        sb.Append(" ");
        
        // 返回类型
        sb.Append(indexer.Type.ToDisplayString());
        sb.Append(" ");
        
        // this[参数列表]
        sb.Append("this[");
        sb.Append(string.Join(", ", indexer.Parameters.Select(p => 
            $"{p.Type.ToDisplayString()} {p.Name}")));
        sb.Append("]");
        
        return sb.ToString();
    }
    
    private string GetAccessibilityString(Accessibility accessibility)
    {
        return accessibility switch
        {
            Accessibility.Public => "public",
            Accessibility.Private => "private",
            Accessibility.Protected => "protected",
            Accessibility.Internal => "internal",
            _ => ""
        };
    }
}

public class IndexerParameter
{
    public string Name { get; set; }
    public string Type { get; set; }
    public RefKind RefKind { get; set; }
    public bool IsOptional { get; set; }
    public object DefaultValue { get; set; }
}

完整示例:属性分析器

csharp
/// <summary>
/// 分析属性的详细信息
/// </summary>
public class PropertyAnalyzer
{
    public PropertyDetails AnalyzeProperty(IPropertySymbol property)
    {
        var details = new PropertyDetails
        {
            Name = property.Name,
            Type = property.Type.ToDisplayString(),
            IsStatic = property.IsStatic,
            IsIndexer = property.IsIndexer,
            Accessibility = property.DeclaredAccessibility
        };
        
        // 访问器分析
        if (property.GetMethod != null)
        {
            details.HasGetter = true;
            details.GetterAccessibility = property.GetMethod.DeclaredAccessibility;
            details.IsGetterAutoImplemented = 
                property.GetMethod.DeclaringSyntaxReferences.Length == 0;
        }
        
        if (property.SetMethod != null)
        {
            details.HasSetter = true;
            details.SetterAccessibility = property.SetMethod.DeclaredAccessibility;
            details.IsSetterAutoImplemented = 
                property.SetMethod.DeclaringSyntaxReferences.Length == 0;
            details.IsInitOnly = property.SetMethod.IsInitOnly;
        }
        
        // 索引器参数
        if (property.IsIndexer)
        {
            details.IndexerParameters = property.Parameters
                .Select(p => new ParameterDetails
                {
                    Name = p.Name,
                    Type = p.Type.ToDisplayString()
                })
                .ToList();
        }
        
        // 重写信息
        if (property.IsOverride && property.OverriddenProperty != null)
        {
            details.OverriddenProperty = 
                property.OverriddenProperty.ToDisplayString();
        }
        
        // 特性
        details.Attributes = property.GetAttributes()
            .Select(a => a.AttributeClass?.Name)
            .Where(n => n != null)
            .ToList();
        
        return details;
    }
}

public class PropertyDetails
{
    public string Name { get; set; }
    public string Type { get; set; }
    public bool IsStatic { get; set; }
    public bool IsIndexer { get; set; }
    public Accessibility Accessibility { get; set; }
    
    public bool HasGetter { get; set; }
    public bool HasSetter { get; set; }
    public Accessibility? GetterAccessibility { get; set; }
    public Accessibility? SetterAccessibility { get; set; }
    public bool IsGetterAutoImplemented { get; set; }
    public bool IsSetterAutoImplemented { get; set; }
    public bool IsInitOnly { get; set; }
    
    public List<ParameterDetails> IndexerParameters { get; set; }
    public string OverriddenProperty { get; set; }
    public List<string> Attributes { get; set; }
}

public class ParameterDetails
{
    public string Name { get; set; }
    public string Type { get; set; }
}

代码生成

生成属性声明代码。

csharp
/// <summary>
/// 生成属性声明代码
/// </summary>
public class PropertyCodeGenerator
{
    /// <summary>
    /// 生成属性声明
    /// </summary>
    public string GeneratePropertyDeclaration(IPropertySymbol property)
    {
        var sb = new StringBuilder();
        
        // 访问修饰符
        sb.Append(GetAccessibilityString(property.DeclaredAccessibility));
        sb.Append(" ");
        
        // 修饰符
        if (property.IsStatic) sb.Append("static ");
        if (property.IsVirtual) sb.Append("virtual ");
        if (property.IsAbstract) sb.Append("abstract ");
        if (property.IsOverride) sb.Append("override ");
        if (property.IsSealed) sb.Append("sealed ");
        
        // 类型
        sb.Append(property.Type.ToDisplayString());
        sb.Append(" ");
        
        // 名称
        if (property.IsIndexer)
        {
            sb.Append("this[");
            sb.Append(string.Join(", ", property.Parameters.Select(p => 
                $"{p.Type.ToDisplayString()} {p.Name}")));
            sb.Append("]");
        }
        else
        {
            sb.Append(property.Name);
        }
        
        // 访问器
        sb.Append(" { ");
        
        if (property.GetMethod != null)
        {
            if (property.GetMethod.DeclaredAccessibility != property.DeclaredAccessibility)
            {
                sb.Append(GetAccessibilityString(property.GetMethod.DeclaredAccessibility));
                sb.Append(" ");
            }
            sb.Append("get; ");
        }
        
        if (property.SetMethod != null)
        {
            if (property.SetMethod.DeclaredAccessibility != property.DeclaredAccessibility)
            {
                sb.Append(GetAccessibilityString(property.SetMethod.DeclaredAccessibility));
                sb.Append(" ");
            }
            
            if (property.SetMethod.IsInitOnly)
                sb.Append("init; ");
            else
                sb.Append("set; ");
        }
        
        sb.Append("}");
        
        return sb.ToString();
    }
    
    /// <summary>
    /// 生成完整的属性(包括后备字段)
    /// </summary>
    public string GenerateFullProperty(
        string propertyName, 
        string propertyType,
        bool hasGetter = true,
        bool hasSetter = true)
    {
        var sb = new StringBuilder();
        
        // 后备字段
        var fieldName = $"_{char.ToLower(propertyName[0])}{propertyName.Substring(1)}";
        sb.AppendLine($"private {propertyType} {fieldName};");
        sb.AppendLine();
        
        // 属性
        sb.AppendLine($"public {propertyType} {propertyName}");
        sb.AppendLine("{");
        
        if (hasGetter)
        {
            sb.AppendLine($"    get => {fieldName};");
        }
        
        if (hasSetter)
        {
            sb.AppendLine($"    set => {fieldName} = value;");
        }
        
        sb.AppendLine("}");
        
        return sb.ToString();
    }
    
    private string GetAccessibilityString(Accessibility accessibility)
    {
        return accessibility switch
        {
            Accessibility.Public => "public",
            Accessibility.Private => "private",
            Accessibility.Protected => "protected",
            Accessibility.Internal => "internal",
            _ => ""
        };
    }
}

最佳实践

✅ 推荐做法

csharp
/// <summary>
/// 属性处理的最佳实践
/// </summary>
public class PropertyBestPractices
{
    // ✅ 1. 检查访问器是否存在
    public void SafeAccessorAccess(IPropertySymbol property)
    {
        // ✅ 正确:检查 null
        if (property.GetMethod != null)
        {
            var getterAccessibility = property.GetMethod.DeclaredAccessibility;
        }
        
        // ❌ 错误:不检查 null
        // var accessibility = property.GetMethod.DeclaredAccessibility; // 可能 NullReferenceException
    }
    
    // ✅ 2. 区分属性和索引器
    public void HandlePropertyOrIndexer(IPropertySymbol property)
    {
        if (property.IsIndexer)
        {
            // 处理索引器
            var parameters = property.Parameters;
        }
        else
        {
            // 处理普通属性
            var name = property.Name;
        }
    }
    
    // ✅ 3. 检查 init-only setter
    public bool IsInitOnlyProperty(IPropertySymbol property)
    {
        return property.SetMethod != null && 
               property.SetMethod.IsInitOnly;
    }
    
    // ✅ 4. 获取后备字段
    public IFieldSymbol GetBackingField(IPropertySymbol property)
    {
        if (!property.IsAutoProperty)
            return null;
        
        var backingFieldName = $"<{property.Name}>k__BackingField";
        return property.ContainingType.GetMembers(backingFieldName)
            .OfType<IFieldSymbol>()
            .FirstOrDefault();
    }
    
    // ✅ 5. 比较访问器的访问级别
    public bool HasDifferentAccessorAccessibility(IPropertySymbol property)
    {
        if (property.GetMethod == null || property.SetMethod == null)
            return false;
        
        return property.GetMethod.DeclaredAccessibility != 
               property.SetMethod.DeclaredAccessibility;
    }
}

❌ 应避免的做法

csharp
/// <summary>
/// 应该避免的反模式
/// </summary>
public class PropertyAntiPatterns
{
    // ❌ 1. 不检查访问器是否存在
    public void WrongAccessorAccess(IPropertySymbol property)
    {
        // ❌ 错误:可能抛出 NullReferenceException
        var accessibility = property.GetMethod.DeclaredAccessibility;
        
        // ✅ 正确
        // if (property.GetMethod != null)
        // {
        //     var accessibility = property.GetMethod.DeclaredAccessibility;
        // }
    }
    
    // ❌ 2. 忽略索引器的参数
    public void WrongIndexerHandling(IPropertySymbol property)
    {
        // ❌ 错误:索引器有参数
        if (property.IsIndexer)
        {
            // 忽略了 property.Parameters
        }
    }
    
    // ❌ 3. 使用字符串比较属性类型
    public bool IsStringPropertyWrong(IPropertySymbol property)
    {
        // ❌ 错误:不可靠
        return property.Type.ToDisplayString() == "string";
        
        // ✅ 正确
        // return property.Type.SpecialType == SpecialType.System_String;
    }
}

🔑 关键要点

  1. 访问器检查: 始终检查 GetMethodSetMethod 是否为 null
  2. 索引器识别: 使用 IsIndexer 属性区分索引器和普通属性
  3. Init-only: 使用 SetMethod.IsInitOnly 检查 init-only setter
  4. 后备字段: 自动属性的后备字段命名为 <PropertyName>k__BackingField
  5. 访问级别: 访问器可以有不同的访问级别

📖 相关文档


🚀 下一步


最后更新: 2026-02-05

基于 MIT 许可发布