静态覆盖/抽象方法 - 任何好的解决方法?

时间:2016-02-17 07:20:53

标签: c#

我有一个抽象的基本配置类和两个实现:

public abstract class BaseConfiguration
{
}

public class LoginConfiguration : BaseConfiguration
{
    public LoginConfiguration() 
    {
    }

    public string Name { get; set; }
    public string Password { get; set; }
}

public class TestConfiguration : BaseConfiguration
{
    public TestConfiguration()
    {
    }
}

我面临的问题: 每个特定的类类型都有一个明确的文件名。这意味着LoginConfiguration有一个名为“login.xml”的文件名,TestConfiguration指向“test.xml”。

我希望稍后用于反序列化的文件名:

static void Main(string[] args)
{
    LoginConfiguration login = ReadFromFile<LoginConfiguration>();
    Console.ReadLine();
}

private static TConfig ReadFromFile<TConfig>() where TConfig : BaseConfiguration
{
    //Something like this needs to be done here:
    string filename = TConfig.GetFilename();

    //Deserialize file and return object
    return Deserialize<TConfig>(filename);
}

但我知道你既不能使用静态覆盖也不能使用静态抽象方法。

我目前正在做的是使用基类来实例化一个新对象并从实例中读取文件名,但那非常hacky。

public abstract class BaseConfiguration
{
    protected BaseConfiguration(string fileName)
    {
        Filename = fileName;
    }

    public string Filename { get; private set; }

    public static string GetFilename<TConfig>() where TConfig : BaseConfiguration, new()
    {
        return new TConfig().Filename;
    }
}

//The calling method:
private static TConfig ReadFromFile<TConfig>() where TConfig : BaseConfiguration, new()
{
    string filename = BaseConfiguration.GetFilename<TConfig>();
    //Deserialize file and return object
    return Deserialize<TConfig>(filename);
}

我现在的问题是: 你有什么想法,我怎样才能更好地设计它?你有更好的方法吗? 在这种问题上C#中的静态抽象方法是否有意义?

提前致谢!

3 个答案:

答案 0 :(得分:7)

如果您不介意反思,可以添加一个属性来提供文件名:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ConfigFileAttribute : Attribute
{
    ...
}

[ConfigFile("login.xml")]
public class LoginConfiguration 
{
    ...
}

由于您已经拥有类型(TConfig),因此您可以使用以下方式访问该属性:

var configAttributes = typeof(TConfig).GetCustomAttributes(typeof(ConfigFileAttribute), false);

当然这意味着忘记该属性会使读取失败。

这不是最好的&#34;解决方案,但我个人更喜欢单独管理文件名,其中类本身对它没有兴趣。

答案 1 :(得分:3)

你的奇怪设计的原因是因为你的XXXConfiguration类打破了单一责任原则,它有一个名为filename的属性,告诉调用者其数据来自何处,以及其他属性来保存加载的数据。 IMO XXXConfiguration类应该只包含数据,没有其数据源的信息。

abstract class BaseConfiguration
{
    public string SharedConfigProperty { get; set; }
}

class LoginConfiguration : BaseConfiguration
{
    public string LoginConfigProperty { get; set; }
}

class TestConfiguration : BaseConfiguration
{
    public string TestConfigProperty { get; set; }
}

在原始设计中,LoginConfiguration只能拥有一个数据源,如果将来有login-dev.xml login-qa.xml,这将是一场噩梦。将配置的加载功能放在另一个类中更好:

class ConfigurationFactory
{
    public static TConfig FromFile<TConfig>() where TConfig : BaseConfiguration
    {
        //you should have a TConfig-fileName mapping
        //e.g. a Dictionary<Type, string>
        //Type is typeof(TConfig) and string is the filename
    }

    public static TConfig FromDataBase<TConfig>() where TConfig : BaseConfiguration
    {
        //as I said, the original design has a lot of restricts
        //what if they change the storage from file to data base?
        //you need to change every derived class, renaming FileName to DataBaseTableName?
    }
}

答案 2 :(得分:1)

是的,我有时希望能够指定合同'如果你扩展这个类,那么你需要提供一个带有这个签名的静态方法',但不幸的是,这不存在。

这不是 更整洁,但我可能会制作一个像这样的文件名的静态地图:

public abstract class BaseConfiguration
{
    static BaseConfiguration()
    {
        Filenames = new Dictionary<Type, string>
        {
            { typeof(LoginConfiguration), "login.xml" },
            { typeof(TestConfiguration), "test.xml" },
        };
    }

    private static Dictionary<Type, string> Filenames { get; }

    public static string GetFilename<TConfig>() where TConfig : BaseConfiguration
    {
        return Filenames[typeof(TConfig)];
    }
}

优点是您在一个地方拥有所有文件名,并且您不需要分配实例来获取文件名。