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

如何更好地封装标记为[Serializable]的C#类成员变量?

封装可序列化数据类的实现方案

针对你遇到的问题——既要用Newtonsoft JSON.NET序列化数据,又要防止外部直接修改PlayerGlobalAggregateData的字段,可以通过以下几步实现封装:

1. 改造数据类:私有字段+JsonProperty标记+受控访问

把原来的public字段改为private,用[JsonProperty]标记让JSON.NET可以序列化/反序列化私有字段,同时提供只读属性供外部获取数据,只暴露受控的修改方法给授权类(比如你的ScriptableObject)。

[Serializable]
public class PlayerGlobalAggregateData
{
    // 用JsonProperty标记私有字段,让JSON.NET能处理序列化
    [JsonProperty("total_score")]
    private int _totalScore;
    [JsonProperty("play_count")]
    private int _playCount;

    // 只读属性,外部只能获取不能修改
    public int TotalScore => _totalScore;
    public int PlayCount => _playCount;

    // 内部修改方法,只允许ScriptableObject等授权类调用
    internal void AddScore(int score)
    {
        _totalScore = Math.Max(0, _totalScore + score); // 可在此添加业务校验逻辑
    }

    internal void IncrementPlayCount()
    {
        _playCount++;
    }
}

2. 优化ScriptableObject:屏蔽直接数据访问,提供受控接口

不再直接返回_data实例,而是暴露只读数据获取方法和受控修改方法,同时内部处理序列化/反序列化逻辑,不让外部拿到可修改的数据对象。

public class PlayerGlobalAggregateModel : ScriptableObject
{
    private PlayerGlobalAggregateData _data;

    // 加载数据时做深拷贝,防止外部传入的对象被修改影响内部状态
    public void SetModelData(PlayerGlobalAggregateData loadedData)
    {
        if (loadedData == null)
        {
            _data = new PlayerGlobalAggregateData();
            return;
        }
        // 用JSON序列化再反序列化实现深拷贝
        _data = JsonConvert.DeserializeObject<PlayerGlobalAggregateData>(
            JsonConvert.SerializeObject(loadedData)
        );
    }

    // 外部获取数据的只读接口
    public int GetTotalScore() => _data.TotalScore;
    public int GetPlayCount() => _data.PlayCount;

    // 受控的修改方法,集中处理业务逻辑
    public void RecordPlayerScore(int score)
    {
        _data.AddScore(score);
    }

    public void LogPlaySession()
    {
        _data.IncrementPlayCount();
    }

    // 内部处理保存逻辑,不让外部接触原始数据
    public void SaveDataToFile(string filePath)
    {
        string json = JsonConvert.SerializeObject(_data, Formatting.Indented);
        File.WriteAllText(filePath, json);
    }
}

3. 可选:用只读接口进一步限制访问

如果需要更严格的访问控制,可以定义一个只读接口,让数据类实现该接口,ScriptableObject只返回接口类型,外部完全无法看到修改方法:

// 只读接口,只包含获取数据的属性
public interface IReadOnlyPlayerData
{
    int TotalScore { get; }
    int PlayCount { get; }
}

// 数据类实现接口
[Serializable]
public class PlayerGlobalAggregateData : IReadOnlyPlayerData
{
    [JsonProperty]
    private int _totalScore;
    [JsonProperty]
    private int _playCount;

    // 实现接口的只读属性
    public int TotalScore => _totalScore;
    public int PlayCount => _playCount;

    // 内部修改方法...
}

// 在ScriptableObject中添加只读数据返回方法
public IReadOnlyPlayerData GetReadOnlyPlayerData() => _data;

核心原理说明

  • [JsonProperty]标记:Newtonsoft JSON.NET支持序列化私有字段,只要用该标签标记,就能正常读写JSON文件,无需保持字段为public。
  • 受控修改:所有数据修改都通过ScriptableObject提供的方法完成,集中处理业务校验(比如分数不能为负),避免外部随意修改导致数据不一致。
  • 深拷贝:加载数据时做深拷贝,防止外部传入的原始对象被修改后影响ScriptableObject内部的状态。

内容的提问来源于stack exchange,提问作者Janesh Parnami

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 03:22:03