配置文件也可以做成强类型,配置文件的强类型其实就是使配置文件也具有int,DateTime也可以是其他自定义的类型,而不是普通的String类型,至于强类型和一般类型的区别与好处,再次不在叙述,此处只讲解详细的制作步骤。由于使用了强类型,任何时候只要发现配置文件不满足条件就会抛异常,保存时不满足条件的数据也是不能通过系统保存的。当然你可以通过记事本修改在保存,但读取时会抛异常。
主要包括:1单个节点2复合节点,也就是有子节点的节点3配置节4读取配置文件。
以下以一个配置文件为例详细讲解
配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="Mail" type="WindowsTestConfig.ClientConfiguration,WindowsTestConfig"/>
</configSections>
<Mail LengTh="8">
<Services ServicesPort="808" >
<Service Address="127.0.0.1" Port="805"/>
<Service Address="127.0.0.2" Port="804"/>
<Service Address="127.0.0.3" Port="803" />
<!--<Service Type="NMail.MetricsService.MetricsService, NMail.MetricsService" />-->
</Services>
</Mail>
</configuration>
1单个节点
Service可以认为是单个节点,应为里边没有包含子节点,只有属性。
<Service Address="127.0.0.1" Port="805"/>
需要注意的是:
1配置文件的属性名称是区分大小写的,ConfigurationProperty的第一个参数必须要和属性名称相同
2需要继承自基类ConfigurationElement,并根据属性的多少添加不同的方法
3对于每一种常见的类型都有对应的验证方法,例如int是IntegerValidator,字符是StringValidator,还有其他的,功能更强大的正则表达式验证类型RegexStringValidator,但个人试了试不知道RegexStringValidator好像不好用。
4把string转化为其他类型,需要有一个继承自TypeConverter的子类的特性,可以看看 [TypeConverter(typeof(IPAddressTypeConverter))],而继承自TypeConverter也很简单,只需要重写两个方法而已。
ConfigurationElement官网参考
对于此处代码为:
/// <summary>
/// A simple class for parsing an IP endpoint out of a configuration file.
/// </summary>
public class IPEndPointElement : ConfigurationElement
{
/// <summary>
/// The endpoint's address.
/// </summary>
[ConfigurationProperty("Address", IsRequired = true)]//Address 需要与配置文件的属性名称相对应,是区分大小写的
[TypeConverter(typeof(IPAddressTypeConverter))]//告诉系统,怎么把一个string类型转化为需要的IPAddress 类型,也就是强类型
// [StringValidator(InvalidCharacters = " ~!@#$%^&*()[]{}/;'\"|\\",MinLength = 7, MaxLength = 15)]//字符串验证属性
// [RegexStringValidator(@"^(\d{1,3}\.){3}\d{1,3}{1}quot;)]//正则表达式验证属性
public IPAddress Address
{
get
{
return (IPAddress)this["Address"];
}
set
{
this["Address"] = value;
}
}
/// <summary>
/// The endpoint's port.
/// </summary>
[ConfigurationProperty("Port", IsRequired = true, DefaultValue = 80)]///Port 需要与配置文件的属性名称相对应,是区分大小写的
[IntegerValidator(MinValue = 1, MaxValue = 65536)]//int 类型验证,设置区间
public int Port
{
get
{
return (int)this["Port"];
}
set
{
this["Port"] = value;
}
}
/// <summary>
/// Converts this instance to an IPEndPoint.
/// </summary>
/// <returns>The endpoint.</returns>
public IPEndPoint ToEndPoint()//返回强类型结果
{
return new IPEndPoint((IPAddress)this["Address"], (int)this["Port"]);
}
public override string ToString()
{
return this["Address"] + ":" + this["Port"];
}
}
/// <summary>
/// A type converter to convert strings to IP addresses.
/// </summary>
public class IPAddressTypeConverter : TypeConverter//把一个string怎样转化为IPAddress,需要继承TypeConverter,并重写一下的两个方法就可以了
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
{
return IPAddress.Parse((string)value);
}
return base.ConvertFrom(context, culture, value);
}
}
2复合节点
Services 可以认为是一个复合节点,因为他包含了一个子节点集合
<Services ServicesPort="808" >
<Service address="127.0.0.1" Port="805"/>
<Service Address="127.0.0.2" Port="804"/>
<Service Address="127.0.0.3" Port="803" />
<!--<Service Type="NMail.MetricsService.MetricsService, NMail.MetricsService" />-->
</Services>
1配置文件的属性名称是区分大小写的,ConfigurationProperty的第一个参数必须要和属性名称相同
2需要继承自基类ConfigurationElementCollection,并根据属性的多少添加不同的方法
3由于是集合多了几个方法需要重写ConfigurationElementCollection官网参考
对应的代码为
public class IPAddressCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()//创建一个写的子节点调用方法
{
return new IPEndPointElement();
}
/// <summary>
/// The endpoint's port.
/// </summary>
[ConfigurationProperty("ServicesPort", IsRequired = true, DefaultValue = 80)]//需要与配置文件一致
[IntegerValidator(MinValue = 1, MaxValue = 65535)]//int 类型的验证
public int ServicesPort
{
get
{
return (int)this["ServicesPort"];
}
set
{
this["ServicesPort"] = value;
}
}
protected override object GetElementKey(ConfigurationElement element)//其他的都是集合相关的操作,只需要很少的改变
{
IPEndPointElement address = (IPEndPointElement)element;
return getKey(address);
}
/// <summary>
/// Gets or sets the IP address element for the given index.
/// </summary>
/// <param name="index">The index of the address to get or set.</param>
/// <returns>The address element.</returns>
public IPEndPointElement this[int index]//
{
get
{
return (IPEndPointElement)BaseGet(index);
}
set
{
if (BaseGet(index) != null)
{
BaseRemove(index);
}
BaseAdd(index, value);
}
}
/// <summary>
/// Gets the number of address in this instance.
/// </summary>
public new int Count
{
get { return base.Count; }
}
public int IndexOf(IPEndPointElement endPoint)
{
return BaseIndexOf(endPoint);
}
public void RemoveAt(int index)
{
BaseRemoveAt(index);
}
public void Add(IPEndPointElement item)
{
BaseAdd(item);
}
public void Clear()
{
BaseClear();
}
public bool Contains(IPEndPointElement item)
{
return BaseIndexOf(item) >= 0;
}
public void CopyTo(IPEndPointElement[] array, int arrayIndex)
{
base.CopyTo(array, arrayIndex);
}
public new bool IsReadOnly
{
get { return false; }
}
public bool Remove(IPEndPointElement item)
{
if (BaseIndexOf(item) >= 0)
{
BaseRemove(item);
return true;
}
return false;
}
public IPAddress[] ToAddressArray()
{
List<IPAddress> addresses = new List<IPAddress>();
for (int i = 0; i < this.Count; i++)
{
addresses.Add(((IPEndPointElement)this[i]).Address);
}
return addresses.ToArray();
}
/// <summary>
/// Gets the key by which address are mapped in the base class.
/// </summary>
/// <param name="endPoint">The address to get the key from.</param>
/// <returns>The key.</returns>
private string getKey(IPEndPointElement address)
{
return address.Address.ToString();
}
}
3配置节
Mail可以认为是一个配置节,因为它外边在没有自定义的东西了
<Mail LengTh="8">
</Mail>
1配置文件的属性名称是区分大小写的,ConfigurationProperty的第一个参数必须要和属性名称相同
2需要继承自基类ConfigurationSection,并根据属性的多少添加不同的方法
3根据包含的子元素写一些方法
4由于自己写的配置节,系统并不知道,所以需要在配置文件中注册。也就是在配置文件中添加如下代码, name是这一配置节的名称,type用逗号分成两部分,前一部分是配置节的类名称包含命名空间,后一部分是配置节类所在的程序集名称。
<configSections>
<section name="Mail" type="WindowsTestConfig.ClientConfiguration,WindowsTestConfig"/>
</configSections>
ConfigurationSection官网参考
对应的代码为:
class ClientConfiguration : ConfigurationSection
{
public static readonly string SectionsName = "Mail";//配置节名称
public static void AddToConfigFile()
{
if (ConfigFile.Current.Sections[SectionsName] == null)//添加配置节
{
ConfigFile.Current.Sections.Add(SectionsName, new ClientConfiguration());
}
}
/// <summary>
/// Returns true if a DNS client configuration section exists in the current
/// configuration.
/// </summary>
public static bool ConfigurationPresent
{
get
{
return (ConfigFile.Current.Sections[SectionsName] != null);
}
}
/// <summary>
/// Returns the current DNS client configuration.
/// </summary>
public static ClientConfiguration Current//获取配置节
{
get
{
ClientConfiguration current;
current = (ClientConfiguration)ConfigFile.Current.GetSection(SectionsName);
if (current == null)
{
throw new ConfigurationErrorsException("Mail configuration is missing");
}
return current;
}
}
/// <summary>
/// The list of DNS servers to attempt lookups on.
/// </summary>
[ConfigurationProperty("Services", IsDefaultCollection = false)]//Services需要与配置文件一致
[ConfigurationCollection(typeof(IPEndPointElement), AddItemName = "Service",
ClearItemsName = "ClearServer", RemoveItemName = "RemoveServer")]//对应集合的几个方法
public IPAddressCollection DnsServers
{
get
{
return (IPAddressCollection)this["Services"];
}
}
[ConfigurationProperty("LengTh", IsRequired = true)]//一个属性,名称需要与配置文件一致
public int LengTh
{
get
{
return (int)this["LengTh"];
}
set
{
this["LengTh"] = value;
}
}
}
4读取配置文件
自己写的配置节系统并不知道,所以需要进行一定处理,配置节类需要用到这个类
处理代码:
class ConfigFile
{
/// <summary>
/// Opens the configuration files and sets up the shared instance of this config.
/// </summary>
static ConfigFile()
{ string path= Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName+".config";//获取配置文件路径包含名称
current = OpenConfig(path);
}
private static Configuration current;
public static Configuration Current {//获取当前节
get {
return current;
}
set {
current = value;
}
}
public static Configuration OpenConfig(string configFilename) {
ExeConfigurationFileMap cfm = new ExeConfigurationFileMap();
cfm.ExeConfigFilename = configFilename;
return ConfigurationManager.OpenMappedExeConfiguration(cfm, ConfigurationUserLevel.None);
}
}
测试:
private void ReadBtn_Click(object sender, EventArgs e)//读取配置文件,如果配置文件不满足条件,读取时就会抛异常
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ClientConfiguration data = config.Sections[ClientConfiguration.SectionsName] as ClientConfiguration;
if (data == null)
{
return;
}
LengThBox.Text= data.LengTh+"";
ServicesPort.Text = data.DnsServers.ServicesPort + "";
if(data.DnsServers.Count>=1)
{
Service1IPBox.Text = data.DnsServers[0].Address.ToString();
Service1PortBox.Text = data.DnsServers[0].Port +"";
Service2IPBox.Text = data.DnsServers[1].Address.ToString();
Service2PortBox.Text = data.DnsServers[1].Port + "";
Service3IPBox.Text = data.DnsServers[2].Address.ToString();
Service3PortBox.Text = data.DnsServers[2].Port + "";
}
}
private void SaveBtn_Click(object sender, EventArgs e)//修改配置文件,如果配置文件不满足条件,写入时时就会抛异常
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ClientConfiguration data = config.Sections[ClientConfiguration.SectionsName] as ClientConfiguration;
if (data == null)
{
return;
}
data.LengTh=int.Parse(LengThBox.Text);
data.DnsServers.ServicesPort = int.Parse(ServicesPort.Text);
if (data.DnsServers.Count >= 1)
{
data.DnsServers[0].Address=IPAddress.Parse(Service1IPBox.Text);
data.DnsServers[0].Port = int.Parse(Service1PortBox.Text);
data.DnsServers[1].Address = IPAddress.Parse(Service2IPBox.Text);
data.DnsServers[1].Port = int.Parse(Service2PortBox.Text);
data.DnsServers[2].Address = IPAddress.Parse(Service3IPBox.Text);
data.DnsServers[2].Port = int.Parse(Service3PortBox.Text);
}
config.Save(ConfigurationSaveMode.Minimal);
}
}
源代码下载