【问题标题】:C# Possible to make generic functions in base class aware of the type of the calling derived class?C# 可以使基类中的泛型函数知道调用派生类的类型吗?
【发布时间】:2021-08-02 04:44:50
【问题描述】:

这里是 OOP 的新手。我想让不同的配置类共享相同的加载和保存功能:

    public abstract class AbstractConfig
    {
        [JsonIgnore]
        public string FileName { get; private set; }

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

        public static T Load<T>(string fileName) where T : AbstractConfig
        {
            T instance = JsonConvert.DeserializeObject<T>(File.ReadAllText(fileName), new JsonSerializerSettings() { ContractResolver = new SubstituteNullWithEmptyStringContractResolver() });
            instance.FileName = fileName;
            return instance;
        }

        public AbstractConfig(string fileName)
        {
            this.FileName = fileName;
        }
    }

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

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

        public static ConfigA Instance { get; set; }

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

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

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

        public static ConfigB Instance { get; set; }

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

我可以这样保存:

ConfigA configA = new ConfigA("a.json");
// set values for config A
configA.Save();

ConfigB configB = new ConfigB("b.json");
// set values for config B
configB.Save();

但是为了加载,我需要这样做:

ConfigA configA = AbstractConfig.Load<ConfigA>("a.json");
ConfigB configB = AbstractConfig.Load<ConfigB>("b.json");

我想知道是否可以让它看起来像这样而不需要覆盖每个派生类中的加载函数:

ConfigA configA = ConfigA.Load("a.json");
ConfigB configB = ConfigB.Load("b.json");

我还想知道是否可以将单例Instance 和构造函数放入基类(我猜仍然会使用泛型?)所以我不需要为每个派生类编写它们。谢谢。

【问题讨论】:

  • abstract class AbstractConfig&lt;T&gt; where T:AbstractConfig&lt;T&gt; ... public static T Load( ... class ConfigA : AbstractConfig&lt;ConfigA&gt; ... 然后你可以ConfigA.Load
  • @JeremyLakeman 我试过了,它确实有效,但我不确定我是否完全理解 AbstractConfig&lt;T&gt; where T : AbstractConfig&lt;T&gt; 部分是如何工作的。您能否详细说明答案(不发表评论),以便我可以选择它作为问题的答案?
  • 也称为“Curiously Recurring Template Pattern”zpbappi.com/curiously-recurring-template-pattern-in-csharp
  • @JeremyLakeman 哇从来不知道它的存在,谢谢提及!

标签: c# oop


【解决方案1】:

abstract class 创建为generic 本身-

public abstract class AbstractConfig<T>
{
    [JsonIgnore]
    public string FileName { get; private set; }

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

    public static T Load(string fileName)
    {
        dynamic instance = JsonConvert.DeserializeObject<T>(File.ReadAllText(fileName), new JsonSerializerSettings());
        instance.FileName = fileName;
        return (T)instance;
    }

    public AbstractConfig(string fileName)
    {
        FileName = fileName;
    }
}

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

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

    public static ConfigA Instance { get; set; }

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

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

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

    public static ConfigB Instance { get; set; }

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

用法-

ConfigA configA = ConfigA.Load("a.json");
ConfigB configB = ConfigB.Load("b.json");

【讨论】:

  • 有趣,似乎动态是另一种解决方案。
  • Dynamic 是重手,尤其是在这个方面。我会不惜一切代价避免使用它。只需向 Load 方法添加一个通用约束,即where T : AbstractConfig&lt;T&gt;,然后您就不需要动态了。 FileName 属性将静态可用
  • 同意。但在这种情况下,类型不会被隐式推断,并且用法就像ConfigA.Load&lt;ConfigA&gt;("a.json"); OP 一开始就不想要的。
  • @pinkfloydx33 实际上我也不喜欢使用动态,除非绝对需要。我最终确实使用了 JeremyLakeman 首次提出的令人困惑的“奇怪重复的模板模式”,并且效果很好。遗憾的是他没有发布作为答案,所以我选择了这个。
  • @whc2001,您也可以使用JToken
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-25
  • 2012-09-08
  • 2014-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
相关资源
最近更新 更多