简体   繁体   English

通过自定义属性设置特定属性的值

[英]Set value of specific property by custom attribute

I am currently developing a software that will be used by users that should not be able to access the back-end of it all but should still be able to easily change configuration/settings for the application. 我目前正在开发一种软​​件,用户将无法访问该软件的后端,但仍应能够轻松更改应用程序的配置/设置。 I decided the best approach would be a custom "configuration file (.cfg)" located in the root of the final build. 我决定最好的方法是位于最终构建的根目录中的自定义“配置文件(.cfg)”。 Simple example of the .cfg file: .cfg文件的简单示例:

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

Since I wanted the configuration file to easily be extended I decided to use some custom attributes and some simple LINQ. 由于我希望配置文件能够轻松扩展,我决定使用一些自定义属性和一些简单的LINQ。 This does work like I expect it to, but since I am still a novice in .net I am afraid I have not gone with the best approach and my question is therefor: Is there anything I can do to improve this? 这确实像我期望的那样工作,但由于我仍然是.net中的新手,我担心我没有采用最佳方法,我的问题是:我有什么办法可以改善这一点吗? Or is there just generally a better approach for this? 或者通常是一种更好的方法吗?

This is my code for reading the configuration file and assigning the values to it's corresponding properties. 这是我的代码,用于读取配置文件并将值分配给它的相应属性。

ConfigFileHandler.cs 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);
            }
        }
    }

ConfigFile.cs ConfigFile.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); }
        }
    }

any tips on writing better looking code would be highly appreciated! 任何关于编写更好看的代码的提示都将受到高度赞赏!


UPDATE: Thanks for all the feedback. 更新:感谢您的所有反馈。

Below is the final classes! 以下是最后的课程! https://dotnetfiddle.net/bPMnJA for a live example https://dotnetfiddle.net/bPMnJA是一个实例

Please note, this is C# 6.0 请注意,这是C#6.0

ConfigFileHandler.cs 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);
    }
 }

ConfigFile.cs ConfigFile.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;
    }
 }

You could keep the simplicity of your config file and get rid of the nested loops by loading the values into a dictionary and then passing that into your ConfigFile class. 您可以保持配置文件的简单性,并通过将值加载到字典中然后将其传递到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);
    }

The new ConfigFile class: 新的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;
    }
}

I decided to use a custom "configuration file (.cfg)" located in the root of the final build. 我决定使用位于最终构建的根目录中的自定义“配置文件(.cfg)”。

Good idea. 好主意。 For cleaner code, you could use JSON and JSON.NET for de/serialization and put the read/write into the ConfigFile class. 为了更清晰的代码,您可以使用JSONJSON.NET进行de / serialization,并将读/写放入ConfigFile类。 Here is an example that is live as a fiddle . 这是一个作为小提琴生活的例子。

The ConfigFile class is responsible for loading and saving itself and uses JSON.NET for de/serialization. ConfigFile类负责加载和保存自身,并使用JSON.NET进行de / serialization。

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);
    }
}

Here is how you would use it to load the file, change its properties, and save. 以下是如何使用它来加载文件,更改其属性和保存。

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

We use the .json file extension as a convention. 我们使用.json文件扩展名作为约定。 You could still use .cfg because it's just plain text with a specific syntax. 您仍然可以使用.cfg因为它只是具有特定语法的纯文本。 The resultant config file content from the above usage is this: 从上面的用法得到的配置文件内容是这样的:

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

You could just tell your clients to "change the numbers only". 您可以告诉您的客户“仅更改数字”。 Your approach is fine, as far as I'm concerned. 就我而言,你的方法很好。 The above is just a cleaner implementation. 以上只是一个更清洁的实现。

Firstly, I would do what Phil did, and store your testlist in a Dictionary. 首先,我会做Phil做的事情,并将您的测试列表存储在字典中。

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

Then you can clean up the property assignment LINQ a bit: 然后你可以清理属性赋值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);
}

You might even be able to call: 你甚至可以打电话给:

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

Don't have an IDE on me so I can't check right now 我没有IDE,所以我现在无法检查

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM