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;
}
}🔑 关键要点
- 访问器检查: 始终检查
GetMethod和SetMethod是否为 null - 索引器识别: 使用
IsIndexer属性区分索引器和普通属性 - Init-only: 使用
SetMethod.IsInitOnly检查 init-only setter - 后备字段: 自动属性的后备字段命名为
<PropertyName>k__BackingField - 访问级别: 访问器可以有不同的访问级别
📖 相关文档
- 返回索引 - 语义模型 API 概览
- INamedTypeSymbol - 类型符号详解
- IMethodSymbol - 方法符号详解
- IFieldSymbol - 字段和参数符号
🚀 下一步
最后更新: 2026-02-05