摘要
在项目开发中经常要读写那些以各种格式存放的配置文件,每次为了读写这些文件都要编写一堆代码,非常麻烦。于是最近终于下定决心,写一个自动读写配置文件的组件,以便达到一劳永逸的效果。 需求
为了达到自动读写的目的,我们需要满足如下需求:1、
对于不同格式的配置文件,只需添加相应的配置模式接口实现,就可以使组件自动读取相同格式的配置文件。2、
可以方便设置不同模式下的参数,比如Ini配置文件,我们需要设置Section, Key,DefautValue等参数。3、
所有的自动读写过程都在基类中封装实现,使用者无需关心配置文件的格式,也无需象过去那样必须从配置文件中将对应的字段一一读出并逐个赋值给实体中对应字段。使用者只需要从基类派生一个实体类,并定义该实体类中的属性字段就可以了。4、
根据配置文件,自动生成实体类的工具5、
支持格式:支持三种通用的配置文件格式,分别是XML,INI和注册表。用户可以参照这三种配置模式,编写自己的特殊格式的模式接口实现。 版本规划
V1.0
实现需求1、2、3。并提供XML,INI两种配置文件格式的模式接口实现V1.1
提供Registry配置格式的模式接口实现V1.2
提供前三种模式的实体类自动生成工具 设计思路

如上图所示整个组件由三部分组成:Configuration 类是所有配置文件实体类的基类。所有实体类都是这个基类的派生类。IConfigurationPattern 是配置模式的接口第三部分是配置模式接口的实现类,目前有两种配置模式 XmlPattern 和 IniPattern。
使用简介
XML 格式实体类的实现和使用
一个简单的实体类例子:using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
using ConfigurationPattern;
namespace CfgSample
{
[ConfigurationPattern(TPattern.XML)]
public class MyXmlCfg: Configuration
{
const String MY_XML_CFG_PATH = @"\myxmlcfg.xml";
public MyXmlCfg()
: base(MY_XML_CFG_PATH)
{
Random rand = new Random();
m_RandId = rand.Next();
}
private String m_Name;
private int m_RandId;
Public Propertys#region Public Propertys
public String Name
{
get
{
return m_Name;
}
set
{
m_Name = value;
}
}
[ConfigurationIgnore]
[XmlIgnore]
public int RandId
{
get
{
return m_RandId;
}
set
{
m_RandId = value;
}
}
#endregion
}
}
这个实体类包括两个公有属性Name 和 RandId,其中RandId不希望配置到文件中,所以用 [ConfigurationIgnore] 和[XmlIgnore]两个属性描述这个字段。其中[ConfigurationIgnore]属性是本组件提供的属性,可以用于描述任何配置模式下的字段属性。
配置文件名在实体类构造中指明,如果你不希望在构造中指明,你也可以在打开实体类之前的任何时候通过FileName属性动态赋值。
使用实体类的示例代码:
MyXmlCfg myXmlCfg = new MyXmlCfg();
myXmlCfg.Open();
Console.WriteLine(String.Format("Name:{0} RandId:{1}", myXmlCfg.Name, myXmlCfg.RandId));
myXmlCfg.Close();
使用实体类很简单,只要打开(Open)实体类,组件会自动将配置文件中的数据读取到实体类中。你可以直接使用。
如果你修改了实体类中的公共属性,比如你调用了如下语句 myXmlCfg.Name="MyName",关闭(Close)实体类时,组件会自动将新的配置写入到配置文件中。
你也可以设置你的实体类为制度,myXmlCfg.ReadOnly = true; 只读的实体类,在关闭时不保存配置。
INI 格式实体类的实现和使用
一个简单的实体类例子:
using System;
using System.Collections.Generic;
using System.Text;
using ConfigurationPattern;
using ConfigurationPattern.Patterns;
namespace CfgSample
{
[ConfigurationPattern(TPattern.INI, "DefaultSection")]
class MyIniCfg : Configuration
{
const String MY_INI_CFG_PATH = @"\myxmlcfg.ini";
public MyIniCfg()
: base(MY_INI_CFG_PATH)
{
Random rand = new Random();
m_RandId = rand.Next();
}
private String m_Name;
private byte m_Age;
private int m_RandId;
private String m_ClassName;
private uint m_Year;
Public Propertys#region Public Propertys
[Section("PrivateInfo")]
public String Name
{
get
{
return m_Name;
}
set
{
m_Name = value;
}
}
[Section("PrivateInfo")]
[Key("Age", 18)]
public byte Age
{
get
{
return m_Age;
}
set
{
m_Age = value;
}
}
[Section("ClassInfo")]
[Key("Class")]
public String ClassName
{
get
{
return m_ClassName;
}
set
{
m_ClassName = value;
}
}
[Section("ClassInfo")]
[Key("Year", 1945)]
public uint Year
{
get
{
return m_Year;
}
set
{
m_Year = value;
}
}
[ConfigurationIgnore]
public int RandId
{
get
{
return m_RandId;
}
set
{
m_RandId = value;
}
}
#endregion
}
}
INI 配置模式,提供如下属性(Attribute)
[ConfigurationPattern(TPattern.INI, "DefaultSection")]
ConfigurationPattern 属性中模式必须指定为TPattern.INI,第二个参数是默认Section,必须要指定!
[Section] 属性,这个属性指定对应字段所属的Section,如[Section("PrivateInfo")],如果不指定这个属性,则认为该字段属于默认Section
[Key] 属性,该属性指定字段在配置文件中的键名,默认值,以及键的大小。如果不指定这个属性,默认为键名为字段名,默认值为实体类初始化时该字段的默认值。
简单使用示例
MyIniCfg myIniCfg = new MyIniCfg();
myXmlCfg.ReadOnly = true;
myIniCfg.Open();
Console.WriteLine(String.Format("Name:{0} Age:{1}, RandId:{2}", myIniCfg.Name, myIniCfg.Age, myIniCfg.RandId));
Console.WriteLine(String.Format("ClassName:{0} Year:{1}", myIniCfg.ClassName, myIniCfg.Year));
myIniCfg.Close();
使用方法和XML模式的实体类是一样的,其实任何模式的实体类使用方法都是一样的。
添加自己的配置模式实现
添加自己的配置模式只需做如下两件事:
1、修改
ConfigurationPatternAttribute.cs 中的
TPattern,增加自定义的模式名称
2、模仿我写的两个模式接口实现,写一个自己的接口实现
源码位置
ConfigurePattern V1.1.1 源码最新更新
该版本为一个补丁版本,修改一处Bug:
Ini 配置模式下,如果被配置的字段为非字符串类型字段,当该字段被配置为空字符串或无效字符时,[Key]属性设置的默认值无效。
如 示例中 Age 字段,如果配置为 "Age=" 或 "Age=abcd",正确结果应该是Age=18(即Age的[Key]属性指定的默认值。但实际结果是 Age=0(这个值是实体类中Age字段的默认值)
V1.0.2版本修改该Bug,当出现无效配置时,首先试图将[Key]属性指定的默认值赋予对应字段,如果再次失败或者没有指定默认值,则使用实体类中该字段的默认值。
V1.0.2 版本源码:
V1.0.2版本
V1.1.1 版本增加一个配置模式 RegistryKeyPattern,用于帮助调用者自动读写注册表配置。另外该版本将
IConfigurationPattern 接口的parameter 参数类型有String改为Object,以便更加通用。
注册表配置的使用:
using System;
using System.Collections.Generic;
using System.Text;
using ConfigurationPattern;
using ConfigurationPattern.Patterns;
using ConfigurationPattern.Patterns.Registry;
using Microsoft.Win32;
namespace CfgSample
{
[ConfigurationPattern(TPattern.REGISTRY, RegistryHive.CurrentUser)]
class MyRegCfg : Configuration
{
String m_Name;
[SubKey(@"software\test")]
public String Name
{
get
{
return m_Name;
}
set
{
m_Name = value;
}
}
int m_Age = 0;
[SubKey(@"software\test", "Age", 18, RegistryValueKind.DWord)]
public int Age
{
get
{
return m_Age;
}
set
{
m_Age = value;
}
}
byte[] m_Data = null;
[HKey(RegistryHive.CurrentUser)]
[SubKey(@"software\test", "Data")]
public byte[] Data
{
get
{
return m_Data;
}
set
{
m_Data = value;
}
}
}
}
上述代码是一个注册表配置类的例子
在类级别需要声明ConfigurationPattern属性。
第一个参数指定模式类型为TPattern.REGISTRY即注册表配置模式。
第二个参数指定默认的HKey,可以不指定默认HKey。如果不指定默认HKey,则每个配置字段都必须用HKey 属性指定对应的HKey。
在字段级别需要
需要指定两个属性HKey和Subkey,其中HKey为可选,如果在类级别指定了默认HKey,字段级别的HKey可以不指定,除非该字段要访问
和默认HKey不同的HKey。
Subkey 属性有4个参数
path:指定键所在路径,该参数必须指定
name:指定键名,可选。如果不指定,键名默认和字段名相同
defaultValue:默认值,可选。
valueKind:键的类型,可选。
注册表配置类的调用示例。
见下面代码,调用方法和其他配置类的调用方法相同。
MyRegCfg myRegCfg = new MyRegCfg();
myRegCfg.Open();
Console.WriteLine(String.Format("Name:{0} Age:{1}", myRegCfg.Name, myRegCfg.Age));
myRegCfg.Age = 37;
myRegCfg.Data = new byte[4];
myRegCfg.Data[0] = 12;
myRegCfg.Close();