You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证属性Setter是否被调用?需编译阶段触发校验的实现方案

如何在C#中校验属性Setter是否被调用(未调用时触发警告/错误)

方案1:构造器强制赋值(从根源避免未调用)

直接在类的构造器中要求传入属性值,确保实例创建时就完成赋值,彻底杜绝Setter没被调用的情况:

public class MyClass
{
    private string _propValue;

    // 构造器强制传入属性值,创建实例时必须提供
    public MyClass(string someProperty)
    {
        ArgumentNullException.ThrowIfNull(someProperty);
        _propValue = someProperty;
    }

    public string SomeProperty
    {
        get => _propValue;
        set
        {
            ArgumentNullException.ThrowIfNull(value);
            _propValue = value;
        }
    }
}

这样创建MyClass实例时必须传参,否则编译报错,直接避免了属性未赋值的问题。

方案2:使用Nullable引用类型(编译时警告)

如果你的项目是C# 8及以上版本,开启Nullable引用类型(在.csproj中设置<Nullable>enable</Nullable>),通过类型系统强制属性必须被赋值:

方式A:Init-only属性(仅初始化时可赋值)

把Setter改成init,确保属性只能在对象初始化阶段赋值,否则编译器会抛出警告:

public class MyClass
{
    private string _propValue;

    public string SomeProperty
    {
        get => _propValue;
        // 仅允许在对象初始化时赋值
        init
        {
            ArgumentNullException.ThrowIfNull(value);
            _propValue = value;
        }
    }
}

使用时:

// 编译警告:未初始化非可空属性'SomeProperty'
var obj = new MyClass();
// 正确写法,无警告
var obj = new MyClass { SomeProperty = "test" };

方式B:构造器初始化校验

保留普通Setter,但确保非可空字段在构造器中初始化,否则编译器会警告字段未初始化:

public class MyClass
{
    // 非可空字段,必须在构造器或初始化器中赋值
    private string _propValue;

    public string SomeProperty
    {
        get => _propValue;
        set
        {
            ArgumentNullException.ThrowIfNull(value);
            _propValue = value;
        }
    }

    public MyClass()
    {
        // 如果这里不赋值,编译器会警告:非可空字段未初始化
        _propValue = string.Empty;
    }
}

方案3:运行时校验(使用属性/执行方法时触发错误)

如果无法在编译阶段强制,可在运行时检查属性是否已赋值,在获取属性或执行核心方法时抛出异常:

public class MyClass
{
    // 用可空类型标记初始未赋值状态
    private string? _propValue;

    public string SomeProperty
    {
        get
        {
            // 获取值时检查,未赋值则抛出异常
            if (_propValue is null)
            {
                throw new InvalidOperationException("SomeProperty尚未赋值,请先设置属性值");
            }
            return _propValue;
        }
        set
        {
            ArgumentNullException.ThrowIfNull(value);
            _propValue = value;
        }
    }

    // 或者在类的核心业务方法中校验
    public void ExecuteBusinessLogic()
    {
        if (_propValue is null)
        {
            throw new InvalidOperationException("执行逻辑前必须先设置SomeProperty");
        }
        // 业务逻辑代码
    }
}

方案4:自定义Roslyn分析器(编译时自定义警告/错误)

如果需要团队级别的强制规范,可编写自定义Roslyn分析器,在编译阶段检测是否设置了目标属性,触发自定义警告或错误。

核心思路是扫描代码中的对象创建表达式,检查是否包含目标属性的初始化:

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class RequiredPropertyAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = "RequiredProperty001";
    private static readonly LocalizableString Title = "必须设置SomeProperty属性";
    private static readonly LocalizableString MessageFormat = "创建{0}实例时必须设置SomeProperty属性";
    private static readonly LocalizableString Description = "确保类实例创建时完成RequiredProperty的赋值";
    private const string Category = "Usage";

    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
        DiagnosticId, Title, MessageFormat, Category, 
        DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

    public override void Initialize(AnalysisContext context)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();

        context.RegisterSyntaxNodeAction(AnalyzeObjectCreation, SyntaxKind.ObjectCreationExpression);
    }

    private void AnalyzeObjectCreation(SyntaxNodeAnalysisContext context)
    {
        var objCreation = (ObjectCreationExpressionSyntax)context.Node;
        var typeInfo = context.SemanticModel.GetTypeInfo(objCreation.Type).Type;
        // 替换为你的目标类名
        if (typeInfo?.Name != "MyClass") return;

        var initializer = objCreation.Initializer;
        if (initializer == null)
        {
            var diagnostic = Diagnostic.Create(Rule, objCreation.GetLocation(), typeInfo.Name);
            context.ReportDiagnostic(diagnostic);
            return;
        }

        var hasRequiredProperty = initializer.Expressions
            .OfType<AssignmentExpressionSyntax>()
            .Any(a => a.Left is IdentifierNameSyntax id && id.Identifier.Text == "SomeProperty");

        if (!hasRequiredProperty)
        {
            var diagnostic = Diagnostic.Create(Rule, initializer.GetLocation(), typeInfo.Name);
            context.ReportDiagnostic(diagnostic);
        }
    }
}

将分析器打包成NuGet包后,项目引用即可在编译时触发自定义警告/错误。


内容的提问来源于stack exchange,提问作者Horman Lewis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 17:35:08