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

C#基类泛型方法如何识别调用派生类类型 省略Load方法重复实现

两个需求都可以通过奇异递归模板模式(CRTP) 实现,将基类改造为泛型基类即可,不需要在每个派生类重复写Load方法、单例定义和重复逻辑。


完整改造代码

1. 泛型抽象基类

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

// 泛型参数T为实际的派生配置类类型
public abstract class AbstractConfig<T> where T : AbstractConfig<T>, new()
{
    [JsonIgnore]
    public string FileName { get; protected set; }

    public void Save()
    {
        File.WriteAllText(FileName, JsonConvert.SerializeObject(this, Formatting.Indented, 
            new JsonSerializerSettings { 
                ContractResolver = new SubstituteNullWithEmptyStringContractResolver() 
            }));
    }

    // 静态Load方法直接返回对应派生类类型,无需调用方指定泛型参数
    public static T Load(string fileName)
    {
        T instance = JsonConvert.DeserializeObject<T>(File.ReadAllText(fileName), 
            new JsonSerializerSettings { 
                ContractResolver = new SubstituteNullWithEmptyStringContractResolver() 
            }) ?? new T();
        instance.FileName = fileName;
        // 自动赋值单例
        Instance = instance;
        return instance;
    }

    // 全局单例,每个派生类独占一份
    public static T Instance { get; set; }

    // 给主动new实例场景用的构造函数
    protected AbstractConfig(string fileName)
    {
        FileName = fileName;
    }

    // 给Json反序列化用的无参构造
    protected AbstractConfig()
    {
    }
}

// 你原有空字符串处理的ContractResolver,保持原来的实现即可
public class SubstituteNullWithEmptyStringContractResolver : DefaultContractResolver
{
    // 原有逻辑不变
}

2. 派生配置类(无需重复写公共逻辑)

public class ConfigA : AbstractConfig<ConfigA>
{
    [JsonProperty("test1")]
    public string Test1 { get; set; }

    [JsonProperty("test2")]
    public int Test2 { get; set; }

    public ConfigA(string fileName) : base(fileName) { }
    public ConfigA() { }
}

public class ConfigB : AbstractConfig<ConfigB>
{
    [JsonProperty("test3")]
    public int Test3 { get; set; }

    [JsonProperty("test4")]
    public string Test4 { get; set; }

    public ConfigB(string fileName) : base(fileName) { }
    public ConfigB() { }
}

使用示例(完全符合你要求的调用方式)

// 加载不需要写基类和泛型参数
ConfigA configA = ConfigA.Load("a.json");
ConfigB configB = ConfigB.Load("b.json");

// 单例直接调用,Load时已经自动赋值
var configASingleton = ConfigA.Instance;
var configBSingleton = ConfigB.Instance;

// 保存逻辑和原有用法完全兼容
ConfigA newConfigA = new ConfigA("a_new.json");
newConfigA.Test1 = "测试内容";
newConfigA.Save();

实现原理

C#中泛型类的静态成员是每个封闭构造类型独立存储的,也就是说AbstractConfig<ConfigA>的Load方法、Instance属性和AbstractConfig<ConfigB>的对应成员完全独立,刚好匹配每个派生配置类的专属静态成员需求,不需要重复实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.09 08:06:01