张腾岳 2025-12-05 01:15 采纳率: 98.6%
浏览 0
已采纳

Property "invChgTime" has no write accessor in C# class

在C#开发中,使用反射或序列化框架(如Newtonsoft.Json、System.Text.Json)时,常遇到“Property 'invChgTime' has no write accessor”错误。该问题通常出现在目标类的 `invChgTime` 属性只有 getter 而无 setter,导致框架无法通过公共写访问器为属性赋值。例如,在反序列化JSON数据到POCO类时,若 `public DateTime invChgTime { get; }` 为只读属性,运行时报错。解决方法包括:添加私有或公共setter(如 `{ get; private set; }`),或使用构造函数参数绑定(适用于System.Text.Json)。需注意兼容性与封装性平衡。
  • 写回答

1条回答 默认 最新

  • 小小浏 2025-12-05 08:49
    关注

    深入解析C#中“Property 'invChgTime' has no write accessor”错误

    1. 问题背景与常见场景

    在C#开发中,尤其是在处理数据传输对象(DTO)或领域模型时,开发者常使用序列化框架如 Newtonsoft.Json 或内置的 System.Text.Json 进行JSON反序列化操作。当目标类中的属性定义为只读(仅有 getter),例如:

    public class InventoryRecord
    {
        public DateTime invChgTime { get; }
    }

    反序列化引擎尝试为 invChgTime 赋值时会抛出异常:Property 'invChgTime' has no write accessor。这是因为大多数序列化器依赖公共 setter 来注入值。

    2. 根本原因分析

    该错误的根本原因在于序列化机制的工作方式:

    • 反射调用:框架通过反射获取属性的 set 方法(写访问器)。
    • 无setter则无法赋值:若属性缺少公共或内部可访问的 setter,则赋值失败。
    • 构造函数绕过限制:部分现代框架支持通过构造函数参数绑定实现只读属性初始化。

    3. 解决方案对比表

    方案适用框架封装性影响代码侵入性推荐程度
    添加 private set;Newtonsoft.Json, System.Text.Json低(仍可控)★★★★☆
    使用构造函数参数绑定System.Text.Json (需配置)高(完全只读)★★★★★
    自定义 JsonConverter两者均支持★★★☆☆
    忽略该属性两者均支持取决于业务逻辑★★☆☆☆

    4. 具体解决方案详解

    4.1 添加 private set 访问器

    最简单直接的方法是修改属性声明:

    public DateTime invChgTime { get; private set; }

    此方式允许序列化器通过私有 setter 写入,同时保持外部不可变性,适用于多数场景。

    4.2 使用构造函数绑定(System.Text.Json)

    对于强调不可变性的设计,可通过启用参数化构造函数绑定:

    public class InventoryRecord
    {
        [JsonConstructor]
        public InventoryRecord(DateTime invChgTime)
        {
            this.invChgTime = invChgTime;
        }
    
        public DateTime invChgTime { get; }
    }

    需确保 JSON 字段名与参数名匹配,或使用 [JsonPropertyName] 显式映射。

    4.3 自定义 JsonConverter 实现深度控制

    当需要复杂逻辑时,可编写自定义转换器:

    public class InventoryRecordConverter : JsonConverter<InventoryRecord>
    {
        public override InventoryRecord Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            var doc = JsonDocument.ParseValue(ref reader);
            var root = doc.RootElement;
            var time = root.GetProperty("invChgTime").GetDateTime();
            return new InventoryRecord(time); // 假设构造函数存在
        }
    
        public override void Write(Utf8JsonWriter writer, InventoryRecord value, JsonSerializerOptions options)
        {
            writer.WriteStartObject();
            writer.WriteString("invChgTime", value.invChgTime);
            writer.WriteEndObject();
        }
    }

    5. 序列化框架行为差异流程图

    graph TD
        A[开始反序列化] --> B{属性是否有公共setter?}
        B -- 是 --> C[通过setter赋值]
        B -- 否 --> D{是否启用构造函数绑定?}
        D -- 是 --> E[调用匹配构造函数]
        D -- 否 --> F{是否存在自定义Converter?}
        F -- 是 --> G[使用Converter处理]
        F -- 否 --> H[抛出异常: no write accessor]
        

    6. 最佳实践建议

    1. 优先考虑业务语义:若属性应为只读,避免暴露公共 setter。
    2. 在性能敏感场景下,使用 System.Text.Json 配合构造函数绑定提升安全性与效率。
    3. 对遗留系统集成,private set; 是最快修复手段。
    4. 统一团队编码规范,明确 DTO 是否允许只读属性及处理策略。
    5. 单元测试中覆盖反序列化路径,防止运行时异常。
    6. 利用源生成器(Source Generators)预生成序列化代码,减少反射开销。
    7. 监控日志中序列化异常,及时发现模型与数据结构不一致问题。
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 12月6日
  • 创建了问题 12月5日