通过自定义属性设置特定 属性 的值
Set value of specific property by custom attribute
我目前正在开发一种软件,供用户使用,这些用户不应能够访问所有软件的后端,但仍应能够轻松更改 configuration/settings 应用程序。
我决定最好的方法是在最终构建的根目录中自定义 "configuration file (.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);
}
}
}
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); }
}
}
任何有关编写更好看代码的提示都将不胜感激!
更新:
感谢所有反馈。
下面是最后的类!
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);
}
}
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;
}
}
您可以保持配置文件的简单性,并通过将值加载到字典中然后将其传递到您的 ConfigFile 中来摆脱嵌套循环 class。
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);
}
新配置文件class:
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.
好主意。对于更简洁的代码,您可以使用 JSON and JSON.NET for de/serialization and put the read/write into the ConfigFile
class. Here is an example that is live as a fiddle.
ConfigFile
class负责自己加载和保存,de/serialization.
使用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"
}
您可以告诉您的客户"change the numbers only"。就我而言,你的方法很好。以上只是一个更简洁的实现。
首先,我会像 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,所以我现在无法查看
我目前正在开发一种软件,供用户使用,这些用户不应能够访问所有软件的后端,但仍应能够轻松更改 configuration/settings 应用程序。 我决定最好的方法是在最终构建的根目录中自定义 "configuration file (.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);
}
}
}
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); }
}
}
任何有关编写更好看代码的提示都将不胜感激!
更新: 感谢所有反馈。
下面是最后的类! 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);
}
}
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;
}
}
您可以保持配置文件的简单性,并通过将值加载到字典中然后将其传递到您的 ConfigFile 中来摆脱嵌套循环 class。
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);
}
新配置文件class:
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.
好主意。对于更简洁的代码,您可以使用 JSON and JSON.NET for de/serialization and put the read/write into the ConfigFile
class. Here is an example that is live as a fiddle.
ConfigFile
class负责自己加载和保存,de/serialization.
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"
}
您可以告诉您的客户"change the numbers only"。就我而言,你的方法很好。以上只是一个更简洁的实现。
首先,我会像 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,所以我现在无法查看