【问题标题】:Set value of specific property by custom attribute通过自定义属性设置特定属性的值
【发布时间】:2015-04-25 00:51:58
【问题描述】:

我目前正在开发一种软​​件,用户将使用该软件,这些用户应该无法访问它的所有后端,但仍应该能够轻松更改应用程序的配置/设置。 我认为最好的方法是在最终构建的根目录中自定义“配置文件 (.cfg)”。 .cfg 文件的简单示例:

serveraddress='10.10.10.10'
serverport='1234'
servertimeout='15000'

由于我希望轻松扩展配置文件,我决定使用一些自定义属性和一些简单的 LINQ。 这确实像我期望的那样工作,但由于我仍然是 .net 的新手,我担心我没有采用最好的方法,我的问题是: 我能做些什么来改善这一点吗? 还是一般来说有更好的方法来解决这个问题?

这是我用于读取配置文件并将值分配给它的相应属性的代码。

ConfigFileHandler.cs

public void ReadConfigFile()
    {
        var cfgFile = new ConfigFile();
        var configLines = File.ReadAllLines("configfile.cfg");
        var testList = configLines.Select(line => line.Split('='))
            .Select(splitString => new Tuple<string, string>(splitString[0], splitString[1].Replace("'", "")))
            .ToList();
        foreach (var prop in typeof(ConfigFile).GetProperties())
        {
            var attrs = (ConfigFileFieldAttribute[])prop.GetCustomAttributes
                (typeof(ConfigFileFieldAttribute), false);
            foreach (var t in from attr in attrs from t in testList where t.Item1 == attr.Name select t)
            {
                prop.SetValue(cfgFile, t.Item2);
            }
        }
    }

配置文件.cs

 class ConfigFile
    {
        private static string _serverAddress;
        private static int _serverPort;
        private static int _serverTimeout;

        [ConfigFileField(@"serveraddress")]
        public string ServerAddress
        {
            get { return _serverAddress; }
            set { _serverAddress= value; }
        }

        [ConfigFileField(@"serverport")]
        public string ServerPort
        {
            get { return _serverPort.ToString(); }
            set { _serverPort= int.Parse(value); }
        }

        [ConfigFileField(@"servertimeout")]
        public string ServerTimeout
        {
            get { return _serverTimeout.ToString(); }
            set { _serverTimeout= int.Parse(value); }
        }
    }

任何关于编写更好看的代码的技巧都将受到高度赞赏!


更新: 感谢所有反馈。

以下是最后一堂课! https://dotnetfiddle.net/bPMnJA 一个活生生的例子

请注意,这是 C# 6.0

ConfigFileHandler.cs

 public class ConfigFileHandler
 {
    public void ReadConfigFile()
    {
        var configLines = File.ReadAllLines("configfile.cfg");
        var configDictionary = configLines.Select(line => line.Split('='))
        .Select(splitString => new Tuple<string, string>(splitString[0],     splitString[1].Replace("'", "")))
        .ToDictionary(kvp => kvp.Item1, kvp => kvp.Item2);
        ConfigFile.SetDictionary(configDictionary);
    }
 }

配置文件.cs

 public class ConfigFile
 {
    private static Dictionary<string, string> _configDictionary;

    public string ServerAddress => PullValueFromConfig<string>("serveraddress", "10.1.1.10");

    public int ServerPort => PullValueFromConfig<int>("serverport", "3306");

    public long ServerTimeout => PullValueFromConfig<long>("servertimeout", "");


    private static T PullValueFromConfig<T>(string key, string defaultValue)
    {
        string value;
        if (_configDictionary.TryGetValue(key, out value) && value.Length > 0)
            return (T) Convert.ChangeType(value, typeof (T));
        return (T) Convert.ChangeType(defaultValue, typeof (T));

    }

    public static void SetDictionary(Dictionary<string, string> configValues)
    {
        _configDictionary = configValues;
    }
 }

【问题讨论】:

  • 为什么要重新发明轮子?配置文件是一个已解决的问题。您可以使用例如XML 或 JSON 序列化,或 .NET .config 文件
  • 感谢您的评论 Thomas Levesque。我知道这些,但我决定不使用它的原因是因为应该能够更改配置文件的用户对这些格式没有经验,因此我试图使配置文件尽可能简单。跨度>
  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。

标签: c# properties configuration attributes


【解决方案1】:

您可以通过将值加载到字典中然后将其传递到您的 ConfigFile 类来保持配置文件的简单性并摆脱嵌套循环。

    public static void ReadConfigFile()
    {
        var configLines = File.ReadAllLines("configfile.cfg");
        var testList = configLines.Select(line => line.Split('='))
            .Select(splitString => new Tuple<string, string>(splitString[0], splitString[1].Replace("'", "")))
            .ToDictionary(kvp => kvp.Item1, kvp => kvp.Item2);

        var cfgFile = new ConfigFile(testList);
    }

新的 ConfigFile 类:

class ConfigFile
{
    private Dictionary<string, string> _configDictionary;

    public ConfigFile(Dictionary<string, string> configValues)
    {
        _configDictionary = configValues;
    }

    public string ServerAddress
    {
        get { return PullValueFromConfig("serveraddress", "192.168.1.1"); }
    }

    public string ServerPort
    {
        get { return PullValueFromConfig("serverport", "80"); }
    }

    public string ServerTimeout
    {
        get { return PullValueFromConfig("servertimeout", "900"); }
    }

    private string PullValueFromConfig(string key, string defaultValue)
    {
            string value;
            if (_configDictionary.TryGetValue(key, out value))
                return value;
            return defaultValue;
    }
}

【讨论】:

  • 谢谢菲尔!这确实是一个非常好的方法!只需添加一些静态值,因为 ConfigFile 数据类将在很多地方初始化。第二个构造函数什么都不做,看起来很完美!我会玩它,如果我有任何问题,请告诉你! :)
【解决方案2】:

我决定使用位于最终构建根目录中的自定义“配置文件 (.cfg)”。

好主意。对于更简洁的代码,您可以使用JSONJSON.NET 进行反序列化并将读/写放入ConfigFile 类。这是一个例子,live as a fiddle

ConfigFile 类负责加载和保存自身,并使用JSON.NET 进行反序列化。

public class ConfigFile
{
    private readonly static string path = "somePath.json";
    public string ServerAddress { get; set; }
    public string ServerPort { get; set; }
    public string ServerTimeout { get; set; }
    public void Save()
    {
        var json = JsonConvert.SerializeObject(this, Formatting.Indented);
        File.WriteAllText(path, json) 
    }
    public static ConfigFile Load()
    {
        var json = File.ReadAllText(path); 
        return JsonConvert.DeserializeObject<ConfigFile>(json);
    }
}

以下是您将如何使用它来加载文件、更改其属性并保存。

ConfigFile f = ConfigFile.Load();
f.ServerAddress = "0.0.0.0";
f.ServerPort = "8080";
f.ServerTimeout = "400";
f.Save();

我们使用.json 文件扩展名作为约定。您仍然可以使用.cfg,因为它只是具有特定语法的纯文本。上述用法生成的配置文件内容如下:

{
    "ServerAddress":"0.0.0.0",
    "ServerPort":"8080",
    "ServerTimeout":"400"
}

您可以告诉您的客户“仅更改数字”。就我而言,您的方法很好。以上只是一个更简洁的实现。

【讨论】:

  • 感谢肖恩的建议!我会试试看!我认为唯一的问题是配置处理程序严格依赖于配置文件的格式是否正确,如果我错了,请纠正我。如果用户错误地不遵循 JSON 格式,而这种格式根本不会分配任何行,这可能会产生问题?
  • @zatixiz 是的。如果用户不遵循 JSON 格式,这将是一个问题。 JSON 比您使用的格式更复杂,因此用户会出错。
  • 我是这么认为的。但是,如果任何参数需要它在数组中的值,JSON 会简化很多,所以 JSON 绝对是需要牢记的东西!再次感谢@Shaun
  • @zatixiz 在最好的情况下,我会创建一个非常简单的 Windows 窗体 GUI,让最终用户以这种方式编辑配置。它只是文件夹根目录中的exe。双击它,它会打开三个文本输入和一个保存按钮。煮熟。 :-)
  • 我发布的代码只是一个示例,最终的配置文件将有 20 多行不同的参数。配置的核心部分确实可以在应用程序的一个简单弹出窗口中进行编辑。但是,配置文件中的某些行不应该对某些用户可见,但同时如果需要,他们有机会更改 .cfg 文件中不可见的行:)
【解决方案3】:

首先,我会做 Phil 所做的事情,并将您的测试列表存储在字典中。

var configLines = File.ReadAllLines("configfile.cfg");
var testDict = configLines.Select(line => line.Split('=', 2))
                          .ToDictionary(s => s[0], s => s[1].Replace("'", ""));

然后你可以稍微清理一下属性赋值LINQ:

foreach (var prop in typeof(ConfigFile).GetProperties())
{
    var attr = prop.GetCustomAttributes(false)
                   .OfType<ConfigFileFieldAttribute>()
                   .FirstOrDefault();
    string val;
    if (attr != null && testDict.TryGetValue(attr.Name, out val))
        prop.SetValue(cfgFile, val);
}

你甚至可以打电话:

var attr = prop.GetCustomAttributes<ConfigFileFieldAttribute>(false).FirstOrDefault();

我没有 IDE,所以我现在无法检查

【讨论】:

  • 美丽的@Psymunn !完美运行,只需将 if (attr != null && testDict.TryGetValue(attr.Name, val)) 更改为 if (attr =! null && testDict.TryGetValue(attr.Name, out val ))。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-26
  • 2020-12-19
  • 2010-10-25
  • 2012-12-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多