属性符号(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();
}💡 最佳实践
检查访问器是否存在
csharp// ✅ 正确:始终检查访问器是否为 null if (property.GetMethod != null) { var accessibility = property.GetMethod.DeclaredAccessibility; } // ❌ 错误:假设访问器总是存在 var accessibility = property.GetMethod.DeclaredAccessibility; // 可能 NullReferenceException使用 IsReadOnly 和 IsWriteOnly 属性
csharp// ✅ 正确:使用便捷属性 if (property.IsReadOnly) { // 处理只读属性 } // ❌ 不推荐:手动检查访问器 if (property.GetMethod != null && property.SetMethod == null) { // 处理只读属性 }区分自动属性和计算属性
csharp// ✅ 正确:使用 IsAutoProperty 区分 if (property.IsAutoProperty) { // 自动属性有编译器生成的后备字段 // 可以通过 AssociatedSymbol 访问 } else { // 计算属性有自定义实现 // 需要分析 getter/setter 的方法体 }处理索引器
csharp// ✅ 正确:检查 IsIndexer 属性 if (property.IsIndexer) { // 索引器有参数 foreach (var param in property.Parameters) { Console.WriteLine($"索引参数: {param.Type} {param.Name}"); } }检查 Init-only 属性
csharp// ✅ 正确:检查 SetMethod 的 IsInitOnly 属性 if (property.SetMethod?.IsInitOnly == true) { // 这是 init-only 属性(C# 9+) // 只能在对象初始化器中设置 }分析访问器的访问级别
csharp// ✅ 正确:分别检查属性和访问器的访问级别 var propertyAccessibility = property.DeclaredAccessibility; var getterAccessibility = property.GetMethod?.DeclaredAccessibility; var setterAccessibility = property.SetMethod?.DeclaredAccessibility; // 访问器可以有不同的访问级别 // 例如:public string Name { get; private set; }处理显式接口实现
csharp// ✅ 正确:检查显式接口实现 if (property.ExplicitInterfaceImplementations.Length > 0) { // 这是显式接口实现的属性 // 名称可能包含接口名称前缀 var interfaceProperty = property.ExplicitInterfaceImplementations[0]; }验证 Required 属性
csharp// ✅ 正确:检查 C# 11+ 的 required 属性 if (property.IsRequired) { // 这个属性必须在对象初始化时设置 // 适用于确保对象创建时的完整性 }处理属性的可空性
csharp// ✅ 正确:检查属性类型的可空注解 var nullableAnnotation = property.Type.NullableAnnotation; if (nullableAnnotation == NullableAnnotation.Annotated) { // 这是可空引用类型属性(如 string?) } else if (nullableAnnotation == NullableAnnotation.NotAnnotated) { // 这是非空引用类型属性(如 string) }分析属性的重写关系
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}");
}🔗 相关文档
本文档集
相关主题
实践指南
📚 下一步
学习完属性符号后,建议继续学习:
字段符号 - fields.md
- 理解字段与属性的关系
- 学习后备字段识别
- 掌握常量值处理
参数符号 - parameters.md
- 学习方法参数分析
- 理解引用参数(ref/out/in)
- 掌握可选参数和默认值
实际应用 - 常见场景
- DTO 映射代码生成
- 序列化器实现
- 数据验证
最后更新: 2026-02-05
文档版本: 1.0