/// <summary>
/// 定义泛型接口
/// </summary>
/// <typeparam name="T">T</typeparam>
public interface ISettingValue<T>
{/// <summary>/// value/// </summary>T DoubleValue { get; }/// <summary>/// key/// </summary>string StringValue { get; }
}/// <summary>/// GenericSetting/// </summary>public abstract class GenericSetting : ISettingValue<double>{/// <summary>/// value[double]/// </summary>public abstract double DoubleValue { get; }/// <summary>/// value[string]/// </summary>public abstract string StringValue { get; }/// <summary>/// value[double[]]/// </summary>public abstract double[] DoubleValues { get; }/// <summary>/// value[double[][]]/// </summary>public abstract double[][] ArrayDoubleValues { get; }/// <summary>/// value[string[]]/// </summary>public abstract string[] StringValues { get; }/// <summary>/// 重写ToString方法,返回GenericSetting拼接json/// </summary>/// <returns></returns>public new abstract string ToString();/// <summary>/// abstract FromString/// </summary>/// <param name="info">string</param>/// <returns></returns>public abstract bool FromString(string info);public static implicit operator string(GenericSetting gs){Setting setting = gs as Setting;if (setting == null){throw new Exception("Not a Setting!");}if (setting.Strings == null && setting.Strings.Length != 0){throw new Exception("Setting has no Strings.");}return setting.Strings[0];}public static implicit operator string[](GenericSetting gs){Setting setting = gs as Setting;if (setting == null){return null;}if (setting.Strings == null){throw new Exception("Setting has no Strings.");}return setting.Strings;}public static implicit operator double(GenericSetting gs){Setting setting = gs as Setting;if (setting == null){throw new Exception("Not a Setting!");}if (setting.Values == null && setting.Values.Length != 0){throw new Exception("Setting has no Values.");}return setting.Values[0];}public static implicit operator int(GenericSetting gs){Setting setting = gs as Setting;if (setting == null){throw new Exception("Not a Setting!");}if (setting.Values == null && setting.Values.Length != 0){throw new Exception("Setting has no Values.");}return (int)setting.Values[0];}public static implicit operator int[](GenericSetting gs){Setting setting = gs as Setting;if (setting == null){return null;}if (setting.Values == null){throw new Exception("Setting has no Values.");}double[] values = setting.Values;int[] array = new int[values.Length];for (int i = 0; i < array.Length; i++){array[i] = (int)values[i];}return array;}public static implicit operator double[](GenericSetting gs){Setting setting = gs as Setting;if (setting == null){return null;}if (setting.Values == null){throw new Exception("Setting has no Values.");}return setting.Values;}public static implicit operator double[][](GenericSetting gs){Setting setting = gs as Setting;if (setting == null){return null;}if (setting.Values == null){throw new Exception("Setting has no Values.");}return setting.ArrayValues;}public static implicit operator GenericSetting(double val){Setting setting = new Setting();setting.Values = new double[1] { val };setting.Strings = new string[0];return setting;}public static implicit operator GenericSetting(string val){Setting setting = new Setting();setting.Values = new double[0];setting.Strings = new string[1] { val };return setting;}public static implicit operator GenericSetting(string[] vals){Setting setting = new Setting();if (vals == null){return setting;}string[] array = new string[vals.Length];for (int i = 0; i < vals.Length; i++){array[i] = vals[i];}setting.Strings = array;setting.Values = new double[0];return setting;}public static implicit operator GenericSetting(double[] vals){Setting setting = new Setting();if (vals == null){return setting;}double[] array = new double[vals.Length];for (int i = 0; i < vals.Length; i++){array[i] = vals[i];}setting.Values = array;setting.Strings = new string[0];return setting;}public static implicit operator GenericSetting(double[][] arrayVals){Setting setting = new Setting();if (arrayVals == null){return setting;}setting.Values = new double[0];setting.Strings = new string[0];setting.ArrayValues = arrayVals;return setting;}/// <summary>/// Initializes a new instance of the <see cref="GenericSetting"/> class./// </summary>protected GenericSetting(){}}/// <summary>/// Setting/// </summary>[Serializable]public class Setting : GenericSetting, ISerializable{/// <summary>/// 附件信息/// </summary>protected Dictionary<string, string> otherInfo;/// <summary>/// double[]/// </summary>protected double[] iVal;/// <summary>/// double[][]/// </summary>protected double[][] iArrayVal;/// <summary>/// string[]/// </summary>protected string[] iStr;/// <summary>/// 时间戳/// </summary>public string TimeStamp => otherInfo["SettingTimeStamp"];/// <summary>/// 协议版本【暂未使用】/// </summary>public int Version => int.Parse(otherInfo["SettingVersion"]);/// <summary>/// Unit/// </summary>public string Units{get{if (otherInfo.ContainsKey("SettingUnits")){return otherInfo["SettingUnits"];}return "NotExist";}}/// <summary>/// 附加信息/// </summary>public Dictionary<string, string> OtherInfo{get{if (otherInfo == null){otherInfo = new Dictionary<string, string>();}return otherInfo;}set{otherInfo = value;}}/// <summary>/// double[]/// </summary>public double[] Values{get{return iVal;}set{iVal = value;OtherInfo["SettingTimeStamp"] = DateTime.Now.ToString("yyyy-M-d tt H:mm:ss.ffffff", CultureInfo.InvariantCulture);OtherInfo["SettingVersion"] = VersionInfo.GetVersion().ToString();}}/// <summary>/// double[][]/// </summary>public double[][] ArrayValues{get{return iArrayVal;}set{iArrayVal = value;OtherInfo["SettingTimeStamp"] = DateTime.Now.ToString("yyyy-M-d tt H:mm:ss.ffffff", CultureInfo.InvariantCulture);OtherInfo["SettingVersion"] = VersionInfo.GetVersion().ToString();}}/// <summary>/// string[]/// </summary>public string[] Strings{get{return iStr;}set{iStr = value;OtherInfo["SettingTimeStamp"] = DateTime.Now.ToString("yyyy-M-d tt H:mm:ss.ffffff", CultureInfo.InvariantCulture);OtherInfo["SettingVersion"] = VersionInfo.GetVersion().ToString();}}/// <summary>/// 获取附件信息/// </summary>/// <param name="key">附加信息key</param>/// <returns></returns>public string this[string key]{get{return otherInfo[key];}set{otherInfo[key] = value;}}/// <summary>/// Initializes a new instance of the <see cref="Setting"/> class./// </summary>public Setting(){otherInfo = new Dictionary<string, string>();iVal = new double[0];iStr = new string[0];iArrayVal = new double[0][];}/// <summary>/// Initializes a new instance of the <see cref="Setting"/> class./// </summary>/// <param name="gs">GenericSetting</param>public Setting(GenericSetting gs){Setting setting = gs as Setting;if (setting == null){throw new Exception("Not a Setting!");}iVal = new double[setting.iVal.Length];setting.iVal.CopyTo(iVal, 0);iStr = new string[setting.iStr.Length];setting.iStr.CopyTo(iStr, 0);otherInfo = new Dictionary<string, string>();string[] array = setting.otherInfo.Keys.ToArray();foreach (string key in array){otherInfo[key] = setting.otherInfo[key];}}/// <summary>/// Initializes a new instance of the <see cref="Setting"/> class. /// </summary>/// <param name="old">Copy</param>public Setting(Setting old){iVal = new double[old.iVal.Length];old.iVal.CopyTo(iVal, 0);iStr = new string[old.iStr.Length];old.iStr.CopyTo(iStr, 0);otherInfo = new Dictionary<string, string>();string[] array = old.otherInfo.Keys.ToArray();foreach (string key in array){otherInfo[key] = old.otherInfo[key];}}public static implicit operator Setting(double val){Setting setting = new Setting();setting.Values = new double[1] { val };setting.Strings = new string[0];return setting;}public static implicit operator Setting(string val){Setting setting = new Setting();setting.Values = new double[0];setting.Strings = new string[1] { val };return setting;}public static implicit operator Setting(string[] vals){Setting setting = new Setting();string[] array = new string[vals.Length];for (int i = 0; i < vals.Length; i++){array[i] = vals[i];}setting.Strings = array;setting.Values = new double[0];return setting;}public static implicit operator Setting(double[] vals){Setting setting = new Setting();double[] array = new double[vals.Length];for (int i = 0; i < vals.Length; i++){array[i] = vals[i];}setting.Values = array;setting.Strings = new string[0];return setting;}public static implicit operator Setting(double[][] vals){Setting setting = new Setting();setting.Values = new double[0];setting.Strings = new string[0];setting.ArrayValues = vals;return setting;}/// <summary>/// SettingTimeStamp to DateTime/// </summary>/// <returns></returns>public DateTime GetTimeStamp(){return DateTime.Parse(OtherInfo["SettingTimeStamp"], CultureInfo.InvariantCulture);}public string[] getOtherInfoKeys(){List<string> list = OtherInfo.Keys.ToList();list.Sort();return list.ToArray();}public string[] getOtherInfoStrings(){Dictionary<string, string> dictionary = OtherInfo;List<string> list = dictionary.Keys.ToList();list.Sort();string[] array = new string[list.Count];for (int i = 0; i < array.Length; i++){array[i] = dictionary[list[i]];}return array;}/// <summary>/// override ToString/// </summary>/// <returns></returns>public override string ToString(){return ToString();}/// <summary>/// ToString with level/// </summary>/// <param name="level">string</param>/// <returns></returns>public string ToString(string level = ""){string text = "{\n" + level + "\t\"OtherInfo\": {\n";bool flag = true;List<string> list = otherInfo.Keys.ToList();list.Sort();foreach (string item in list){if (flag){flag = false;}else{text += ",\n";}text = text + level + "\t\t\"" + item + "\":\"" + otherInfo[item] + "\"";}text = text + "\n" + level + "\t},\n";text = text + level + "\t\"Values\": [";string text2 = "";for (int i = 0; i < iVal.Length; i++){text = (!double.IsPositiveInfinity(iVal[i])) ? ((!double.IsNegativeInfinity(iVal[i])) ? ((!double.IsNaN(iVal[i])) ? (text + text2 + iVal[i]) : (text + text2 + "NaN")) : (text + text2 + "-Infinity")) : (text + text2 + "Infinity");text2 = ",";}text += "],\n";text = text + level + "\t\"Strings\": [";text2 = "";for (int j = 0; j < iStr.Length; j++){text = text + text2 + "\"" + iStr[j].Replace("\\", "\\\\") + "\"";text2 = ",";}return text + "]\n" + level + "}";}/// <summary>/// FromString/// </summary>/// <param name="info">string</param>/// <returns></returns>public override bool FromString(string info){try{string text = "\"OtherInfo\": {";int num = info.IndexOf(text) + text.Length;int num2 = info.IndexOf("}", num);string text2 = info.Substring(num, num2 - num);List<string> list = new List<string>();while (text2.Contains("\"")){int num3 = text2.IndexOf("\"");int num4 = text2.IndexOf("\"", num3 + 1);list.Add(text2.Substring(num3 + 1, num4 - num3 - 1));text2 = text2.Substring(num4 + 1);}for (int i = 0; i < list.Count; i += 2){otherInfo[list[i]] = list[i + 1];}text = "\"Values\": [";int num5 = info.IndexOf(text) + text.Length;int num6 = info.IndexOf("]", num5);string text3 = info.Substring(num5, num6 - num5);List<string> list2 = new List<string>();while (text3.Contains(",")){int num7 = text3.IndexOf(",");if (num7 == -1){break;}list2.Add(text3.Substring(0, num7));text3 = text3.Substring(num7 + 1);}if (text3 != ""){list2.Add(text3);}iVal = new double[list2.Count];for (int j = 0; j < list2.Count; j++){if (!double.TryParse(list2[j], out iVal[j])){if (list2[j] == "Infinity"){iVal[j] = double.PositiveInfinity;}else if (list2[j] == "-Infinity"){iVal[j] = double.NegativeInfinity;}else{iVal[j] = double.NaN;}}}text = "\"Strings\": [";num5 = info.IndexOf(text) + text.Length;num6 = info.IndexOf("]", num5);text3 = info.Substring(num5, num6 - num5);list2 = new List<string>();while (text3.Contains(",")){int num8 = text3.IndexOf(",");if (num8 == -1){break;}list2.Add(text3.Substring(0, num8));text3 = text3.Substring(num8 + 1);}if (text3 != ""){list2.Add(text3);}iStr = new string[list2.Count];for (int k = 0; k < list2.Count; k++){iStr[k] = list2[k].Substring(1, list2[k].Length - 2).Replace("\\\\", "\\");if (iStr[k].Contains("\"")){iStr[k] = iStr[k].Substring(iStr[k].IndexOf("\"") + 1);if (iStr[k].Contains("\"")){iStr[k] = iStr[k].Substring(0, iStr[k].LastIndexOf("\""));}}}}catch{return false;}return true;}/// <summary>/// Initializes a new instance of the <see cref="Setting"/> class. 反序列化构造方法[实现反序列化]/// </summary>/// <param name="info">SerializationInfo</param>/// <param name="context">StreamingContext</param>protected Setting(SerializationInfo info, StreamingContext context){otherInfo = new Dictionary<string, string>();string[] array = info.GetValue("otherInfoKeys", typeof(string[])) as string[];string[] array2 = info.GetValue("otherInfoValues", typeof(string[])) as string[];for (int i = 0; i < array.Length; i++){otherInfo[array[i]] = array2[i];}iVal = info.GetValue("iVal", typeof(double[])) as double[];iStr = info.GetValue("iStr", typeof(string[])) as string[];iArrayVal = info.GetValue("iArrayVal", typeof(double[][])) as double[][];}/// <summary>/// 序列化方法/// </summary>/// <param name="info">SerializationInfo</param>/// <param name="context">StreamingContext</param>public virtual void GetObjectData(SerializationInfo info, StreamingContext context){info.AddValue("iStr", iStr);info.AddValue("iVal", iVal);info.AddValue("iArrayVal", iArrayVal);info.AddValue("otherInfoKeys", otherInfo.Keys.ToArray());info.AddValue("otherInfoValues", otherInfo.Values.ToArray());}/// <summary>/// double/// </summary>public override double DoubleValue => Values?.Length > 0 ? Values[0] : default;/// <summary>/// string/// </summary>public override string StringValue => Strings?.Length > 0 ? Strings[0] : default;/// <summary>/// double[]/// </summary>public override double[] DoubleValues => Values;/// <summary>/// string[]/// </summary>public override string[] StringValues => Strings;/// <summary>/// double[][]/// </summary>public override double[][] ArrayDoubleValues => ArrayValues;}[Serializable]public class SettingContainer : Setting, ISerializable{public Dictionary<string, SettingContainer> SettingsContainers;public Dictionary<string, Setting> Settings;private static string delimiter;private static List<string> m_OtherInfoKeys;[NonSerialized]private string[] skeys;[NonSerialized]private Setting[] sValues;[NonSerialized]private string[] sckeys;[NonSerialized]private SettingContainer[] scValues;[NonSerialized]private bool isDeserialized;/// <summary>/// split "."/// </summary>public static string Delimiter => delimiter;/// <summary>/// get/set value with key/// </summary>/// <param name="key">string</param>/// <returns></returns>public GenericSetting this[string key]{get{int num = key.IndexOf(delimiter);if (num > 0){string text = key.Substring(0, num);string text2 = key.Substring(num + 1);SettingContainer settingContainer = null;if (SettingsContainers != null && SettingsContainers.ContainsKey(text)){settingContainer = SettingsContainers[text];try{return settingContainer[text2];}catch (Exception innerException){throw new Exception("Can not find key " + key + " (failed in" + text2 + ")", innerException);}}throw new Exception("Can not find key " + key + " (missing " + text + ")");}if (Settings != null && Settings.ContainsKey(key)){return Settings[key];}if (SettingsContainers == null || !SettingsContainers.ContainsKey(key)){throw new Exception("Can't find key " + key);}return SettingsContainers[key];}set{int num = key.IndexOf(delimiter);if (num > 0){string key2 = key.Substring(0, num);string key3 = key.Substring(num + 1);if (SettingsContainers == null){SettingsContainers = new Dictionary<string, SettingContainer>();}if (!SettingsContainers.ContainsKey(key2)){SettingsContainers[key2] = new SettingContainer();}SettingsContainers[key2][key3] = value;}else if (value is Setting){if (Settings == null){Settings = new Dictionary<string, Setting>();}Settings[key] = value as Setting;}else if (value is SettingContainer){if (SettingsContainers == null){SettingsContainers = new Dictionary<string, SettingContainer>();}SettingsContainers[key] = value as SettingContainer;}else{if (value != null){throw new Exception("value can not be set into " + key);}if (Settings != null && Settings.ContainsKey(key)){Settings.Remove(key);}if (SettingsContainers != null && SettingsContainers.ContainsKey(key)){SettingsContainers.Remove(key);}}}}/// <summary>/// Initializes a new instance of the <see cref="SettingContainer"/> class. /// </summary>public SettingContainer(){}/// <summary>/// Initializes a new instance of the <see cref="SettingContainer"/> class./// </summary>/// <param name="old">SettingContainer</param>public SettingContainer(SettingContainer old){if (old.SettingsContainers != null){SettingsContainers = new Dictionary<string, SettingContainer>();string[] array = old.SettingsContainers.Keys.ToArray();foreach (string key in array){SettingsContainers[key] = new SettingContainer(old.SettingsContainers[key]);}}if (old.Settings != null){Settings = new Dictionary<string, Setting>();string[] array = old.Settings.Keys.ToArray();foreach (string key2 in array){Settings[key2] = new Setting(old.Settings[key2]);}}}public static implicit operator SettingContainer(Dictionary<string, double[]> dictionarySettings){SettingContainer settingContainer = new SettingContainer();settingContainer.updateSettings(dictionarySettings);return settingContainer;}public static implicit operator SettingContainer(Dictionary<string, string[]> dictionarySettings){SettingContainer settingContainer = new SettingContainer();settingContainer.updateStringSettings(dictionarySettings);return settingContainer;}public static implicit operator Dictionary<string, double[]>(SettingContainer gs){return gs.GetDictionary();}public static implicit operator Dictionary<string, string[]>(SettingContainer gs){return gs.GetStringDictionary();}/// <summary>/// ContainsKey/// </summary>/// <param name="key">string</param>/// <returns>bool</returns>public bool ContainsKey(string key){return Keys().Contains(key);}/// <summary>/// get keys/// </summary>/// <param name="superKey">superKey</param>/// <returns>List<string></returns>public List<string> Keys(string superKey = ""){List<string> list = new List<string>();if (SettingsContainers != null){foreach (string key in SettingsContainers.Keys){foreach (string item in SettingsContainers[key].Keys(superKey + key + ".")){list.Add(item);}}}if (Settings != null){foreach (string key2 in Settings.Keys){list.Add(superKey + key2);}return list;}return list;}public GenericSetting NullableGet(string key){int num = key.IndexOf(delimiter);if (num > 0){string key2 = key.Substring(0, num);string key3 = key.Substring(num + 1);if (SettingsContainers != null && SettingsContainers.ContainsKey(key2)){return SettingsContainers[key2]?.NullableGet(key3);}return null;}if (Settings != null && Settings.ContainsKey(key)){return Settings[key];}if (SettingsContainers != null){if (SettingsContainers.ContainsKey(key)){return SettingsContainers[key];}return null;}return null;}public Dictionary<string, Setting> GetSettingDictionary(){Dictionary<string, Setting> dictionary = new Dictionary<string, Setting>();if (Settings != null){foreach (string key in Settings.Keys){dictionary[key] = Settings[key];}}Dictionary<string, SettingContainer> dictionary2 = null;bool flag;if (flag = SettingsContainers != null){dictionary2 = SettingsContainers;}while (flag){Dictionary<string, SettingContainer> dictionary3 = new Dictionary<string, SettingContainer>();new List<SettingContainer>();foreach (string key2 in dictionary2.Keys){SettingContainer settingContainer = dictionary2[key2];if (settingContainer.SettingsContainers != null){foreach (string key3 in settingContainer.SettingsContainers.Keys){dictionary3[key2 + delimiter + key3] = settingContainer.SettingsContainers[key3];}}if (settingContainer.Settings == null){continue;}foreach (string item in settingContainer.Settings.Keys.ToList()){dictionary[key2 + delimiter + item] = settingContainer.Settings[item];}}dictionary2 = dictionary3;flag = dictionary2.Count > 0;}return dictionary;}public Dictionary<string, double[]> GetDictionary(){Dictionary<string, double[]> dictionary = new Dictionary<string, double[]>();Dictionary<string, Setting> settingDictionary = GetSettingDictionary();foreach (string key in settingDictionary.Keys){if (settingDictionary[key].Values.Length != 0){dictionary[key] = new double[settingDictionary[key].Values.Length];settingDictionary[key].Values.CopyTo(dictionary[key], 0);}}return dictionary;}public Dictionary<string, string[]> GetStringDictionary(bool withOtherInfo = true){Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>();Dictionary<string, Setting> settingDictionary = GetSettingDictionary();foreach (string key in settingDictionary.Keys){if (withOtherInfo){foreach (string key2 in settingDictionary[key].OtherInfo.Keys){dictionary[key + delimiter + key2] = new string[1] { settingDictionary[key].OtherInfo[key2] };}}if (settingDictionary[key].Strings.Length != 0){dictionary[key] = new string[settingDictionary[key].Strings.Length];settingDictionary[key].Strings.CopyTo(dictionary[key], 0);}}return dictionary;}public void updateSettings(Dictionary<string, double[]> DictionarySettings){Dictionary<string, Setting> settingDictionary = GetSettingDictionary();foreach (string key in DictionarySettings.Keys){try{Setting setting = null;setting = (Setting)(settingDictionary.ContainsKey(key) ? settingDictionary[key] : (this[key] = new Setting()));setting.Values = DictionarySettings[key].ToArray();}catch (Exception innerException){throw new Exception("Error with key " + key, innerException);}}}public void updateStringSettings(Dictionary<string, string[]> DictionarySettings){Dictionary<string, Setting> settingDictionary = GetSettingDictionary();foreach (string key in DictionarySettings.Keys){string text = null;string text2 = null;try{int num = key.LastIndexOf(delimiter);if (num > 0){text = key.Substring(num + 1);}if (m_OtherInfoKeys.Contains(text)){text2 = key.Substring(0, num);}else{text = null;text2 = key;}Setting setting = null;setting = (Setting)(settingDictionary.ContainsKey(text2) ? settingDictionary[text2] : (this[text2] = (settingDictionary[text2] = new Setting())));if (text == null){string[] source = DictionarySettings[text2];setting.Strings = source.ToArray();}else if (DictionarySettings[key].Length != 0){setting.OtherInfo[text] = DictionarySettings[key][0];}}catch{throw;}}}/// <summary>/// Initializes a new instance of the <see cref="SettingContainer"/> class. 反序列化构造方法[实现反序列化]/// </summary>/// <param name="info">SerializationInfo</param>/// <param name="context">StreamingContext</param>protected SettingContainer(SerializationInfo info, StreamingContext context){skeys = info.GetValue("DictionarySettingsKeys", typeof(string[])) as string[];sValues = info.GetValue("DictionarySettingsValues", typeof(Setting[])) as Setting[];sckeys = info.GetValue("DictionarySettingContainersKeys", typeof(string[])) as string[];scValues = info.GetValue("DictionarySettingContainersValues", typeof(SettingContainer[])) as SettingContainer[];}/// <summary>/// 序列化方法/// </summary>/// <param name="info">SerializationInfo</param>/// <param name="context">StreamingContext</param>public virtual void GetObjectData(SerializationInfo info, StreamingContext context){if (Settings != null){info.AddValue("DictionarySettingsKeys", Settings.Keys.ToArray());info.AddValue("DictionarySettingsValues", Settings.Values.ToArray());}else{info.AddValue("DictionarySettingsKeys", null);info.AddValue("DictionarySettingsValues", null);}if (SettingsContainers != null){info.AddValue("DictionarySettingContainersKeys", SettingsContainers.Keys.ToArray());info.AddValue("DictionarySettingContainersValues", SettingsContainers.Values.ToArray());}else{info.AddValue("DictionarySettingContainersKeys", null);info.AddValue("DictionarySettingContainersValues", null);}}[OnDeserialized]internal void OnDeserialized(StreamingContext context){if (skeys != null){Settings = new Dictionary<string, Setting>();for (int i = 0; i < skeys.Length; i++){Settings[skeys[i]] = sValues[i];}skeys = null;sValues = null;}if (sckeys != null){SettingsContainers = new Dictionary<string, SettingContainer>();for (int j = 0; j < sckeys.Length; j++){SettingsContainers[sckeys[j]] = scValues[j];}sckeys = null;scValues = null;}isDeserialized = true;}public override string ToString(){return ToString("");}public string ToString(string level){string text = "{\n";bool flag = true;if (SettingsContainers != null){text = text + level + "\t\"SettingsContainers\": {\n";List<string> list = SettingsContainers.Keys.ToList();list.Sort();foreach (string item in list){if (flag){flag = false;}else{text += ",\n";}text = text + level + "\t\t\"" + item + "\": ";text += SettingsContainers[item].ToString(level + "\t\t");}text = text + "\n\t" + level + "},\n";}else{text = text + level + "\t\"SettingsContainers\": null,\n";}flag = true;if (Settings != null){text = text + level + "\t\"Settings\": {\n";List<string> list2 = Settings.Keys.ToList();list2.Sort();foreach (string item2 in list2){if (flag){flag = false;}else{text += ",\n";}text = text + level + "\t\t\"" + item2 + "\": ";text += Settings[item2].ToString(level + "\t\t");}text = text + "\n" + level + "\t}\n";}else{text = text + level + "\t\"Settings\": null\n";}return text + level + "}";}public override bool FromString(string info){return FromString2(info, "");}private bool FromString2(string info, string settingOrContainer){try{if (settingOrContainer == "settings"){SettingsContainers = null;Settings = new Dictionary<string, Setting>();}if (settingOrContainer == "SettingsContainers"){SettingsContainers = new Dictionary<string, SettingContainer>();Settings = null;}int num = 0;while (num > -1 && num < info.Length){int num2 = info.IndexOf("\"", num);if (num2 < 0){break;}int num3 = info.IndexOf("\"", num2 + 1);string text = info.Substring(num2 + 1, num3 - num2 - 1);int num4 = info.IndexOf("{", num3 + 1);int num5 = num4;int num6 = 1;int num7 = 0;int num8 = info.IndexOf(",", num3 + 1);if (num8 < num4){num = num8;continue;}if (num4 < 0){break;}while (num7 < num6){int num9 = info.IndexOf("{", num5 + 1);int num10 = info.IndexOf("}", num5 + 1);if (num9 < 0){num9 = info.Length;}if (num10 < 0){break;}if (num9 < num10){num6++;num5 = num9;}else{num7++;num5 = num10;}}string info2 = info.Substring(num4, num5 - num4 + 1);if (text == "Settings"){settingOrContainer = "settings";SettingContainer settingContainer = new SettingContainer();try{settingContainer.FromString2(info2, settingOrContainer);}catch{settingContainer = null;}Settings = settingContainer.Settings;}else if (settingOrContainer == "settings"){Setting setting = new Setting();try{setting.FromString(info2);}catch{setting = null;}Settings[text] = setting;}else if (text == "SettingsContainers"){settingOrContainer = "SettingsContainers";SettingContainer settingContainer2 = new SettingContainer();try{settingContainer2.FromString2(info2, settingOrContainer);}catch{settingContainer2 = null;}SettingsContainers = settingContainer2.SettingsContainers;}else if (settingOrContainer == "SettingsContainers"){SettingContainer settingContainer3 = new SettingContainer();try{settingContainer3.FromString(info2);}catch{settingContainer3 = null;}SettingsContainers[text] = settingContainer3;}num = num5 + 1;}}catch{return false;}return true;}public Dictionary<string, string> compare(SettingContainer other, bool swap = true, double tolerance = 0.0){Dictionary<string, string> dictionary = (!swap) ? new Dictionary<string, string>() : other.compare(this, swap: false);Dictionary<string, double[]> dictionary2 = GetDictionary();Dictionary<string, string[]> stringDictionary = GetStringDictionary();Dictionary<string, double[]> dictionary3 = other.GetDictionary();Dictionary<string, string[]> stringDictionary2 = other.GetStringDictionary();foreach (string key in dictionary2.Keys){double[] array = dictionary2[key];if (!dictionary3.ContainsKey(key)){dictionary[key] = "missing key";continue;}double[] array2 = dictionary3[key];if (array.Length != array2.Length){dictionary[key] = "mismatched size";continue;}for (int i = 0; i < array.Length; i++){if (double.IsNaN(array[i]) != double.IsNaN(array2[i]) && Math.Abs(array[i] - array2[i]) <= tolerance){dictionary[key + ";" + i] = "mismatched value " + array[i] + " != " + array2[i];}}}foreach (string key2 in stringDictionary.Keys){if (key2.Contains("SettingTimeStamp") || key2.Contains("AcuShape.PROCESSING.version")){continue;}if (!stringDictionary2.ContainsKey(key2)){dictionary[key2] = "missing key";continue;}if (stringDictionary[key2].Length != stringDictionary2[key2].Length){dictionary[key2] = "mismatched size";continue;}for (int j = 0; j < stringDictionary[key2].Length; j++){if (stringDictionary[key2][j] != stringDictionary2[key2][j]){dictionary[key2 + ";" + j] = "mismatched value";}}}return dictionary;}static SettingContainer(){delimiter = ".";m_OtherInfoKeys = new List<string> { "SettingTimeStamp", "SettingUnits", "SettingVersion" };}}public class VersionInfo{public const string Version = "1.0.0.0";public static string GetVersion(){return Version;}public VersionInfo(){}}
配置数据管理体系:ISettingValue<T>
、GenericSetting
、Setting
与 SettingContainer
解析
一、体系整体定位与核心功能
该代码片段构建了一套标准化的配置数据管理体系,核心用于工业控制、设备参数配置或系统设置等场景中,实现 “多类型数据统一存储、序列化 / 反序列化、灵活访问” 的需求。整个体系通过 “接口 - 抽象类 - 具体类 - 容器类” 的层级设计,覆盖了从单一配置项到复杂嵌套配置的全场景,支持 double
、string
及多维数组类型,并提供 JSON 格式转换、序列化、数据对比等核心能力。
二、核心组件与层级关系
整个体系的层级结构清晰,各组件职责单一且协同配合,具体关系如下:
ISettingValue 泛型接口
GenericSetting 抽象类
Setting 具体配置类
SettingContainer 配置容器类
嵌套存储 Setting/SettingContainer
各组件的核心定位:
ISettingValue<T>
:定义配置项的基础访问接口,约束 “值(DoubleValue
)” 和 “键(StringValue
)” 的获取方式。GenericSetting
:抽象基类,扩展接口能力,支持多维数据(double[]
/double[][]
/string[]
),并提供隐式类型转换(如GenericSetting
与double
/string
/ 数组的互转)。Setting
:具体配置实现类,支持序列化(ISerializable
)、JSON 格式转换(ToString
/FromString
),并存储配置的附加信息(时间戳、版本、单位等)。SettingContainer
:配置容器类,基于字典结构存储多个Setting
或嵌套SettingContainer
,支持 “键路径访问”(如container["parent.child.key"]
),解决复杂嵌套配置的管理问题。
三、核心组件详解
1. 泛型接口:ISettingValue<T>
核心作用
定义配置项的最小功能契约,约束所有配置类必须实现 “值” 和 “键” 的获取逻辑,其中 T
默认为 double
(由 GenericSetting
实现时指定)。
关键成员
成员 | 类型 | 作用说明 |
---|---|---|
DoubleValue | T (double ) | 获取配置的数值型值(核心数据) |
StringValue | string | 获取配置的字符串型键 / 描述(标识) |
设计意义
通过接口标准化配置项的访问方式,确保不同配置类的一致性,便于后续扩展和统一管理。
2. 抽象基类:GenericSetting
核心作用
作为 ISettingValue<double>
的实现者和 Setting
的基类,扩展多维数据支持和类型转换能力,是整个体系的 “功能中转站”。
关键成员与能力
(1)多维数据访问(抽象属性)
定义了覆盖单值、一维数组、二维数组的抽象属性,强制子类(Setting
)实现数据存储逻辑:
DoubleValue
:单值double
(实现接口)StringValue
:单值string
(实现接口)DoubleValues
:double[]
(一维数值数组)ArrayDoubleValues
:double[][]
(二维数值数组)StringValues
:string[]
(一维字符串数组)
(2)隐式类型转换(核心亮点)
提供 11 种隐式转换,支持 GenericSetting
与常见类型的无缝互转,极大简化代码编写。例如:
GenericSetting → double
:直接获取配置的第一个数值(Values[0]
)double → GenericSetting
:将单值double
封装为Setting
对象string[] → GenericSetting
:将字符串数组封装为Setting
对象GenericSetting → double[][]
:直接获取二维数值数组(ArrayValues
)
转换示例代码:
csharp
// double 隐式转为 GenericSetting
GenericSetting gs1 = 123.45;
Console.WriteLine(gs1.DoubleValue); // 输出 123.45// string[] 隐式转为 GenericSetting
GenericSetting gs2 = new string[] { "name", "age" };
Console.WriteLine(gs2.StringValues[1]); // 输出 "age"// GenericSetting 隐式转为 double[]
double[] values = gs1;
Console.WriteLine(values[0]); // 输出 123.45
(3)序列化基础方法
定义抽象方法 ToString()
(JSON 序列化)和 FromString(string info)
(JSON 反序列化),强制子类实现数据与字符串的转换逻辑。
3. 具体配置类:Setting
核心作用
GenericSetting
的具体实现,是 “单一配置项” 的最终载体,支持序列化、附加信息存储、JSON 解析,是体系中最常用的基础配置单元。
关键成员与能力
(1)核心数据存储字段
字段 | 类型 | 作用说明 |
---|---|---|
iVal | double[] | 存储数值型数据(对应 DoubleValues ) |
iArrayVal | double[][] | 存储二维数值型数据(对应 ArrayDoubleValues ) |
iStr | string[] | 存储字符串型数据(对应 StringValues ) |
otherInfo | Dictionary<string, string> | 存储附加信息(时间戳、版本、单位等) |
(2)附加信息管理(实用功能)
通过 otherInfo
字典存储配置的元数据,提供便捷访问属性:
TimeStamp
:配置更新时间戳(格式:yyyy-M-d tt H:mm:ss.ffffff
)Version
:配置版本(来自VersionInfo
,默认1.0.0.0
)Units
:数据单位(如 “℃”“m/s”,默认 “NotExist”)- 索引器
this[string key]
:直接访问附加信息(如setting["SettingUnits"]
)
(3)JSON 序列化 / 反序列化(核心功能)
ToString(string level = "")
:将配置数据(含附加信息、数值、字符串)转换为格式化 JSON 字符串,支持缩进(level
参数控制缩进层级),便于人类阅读和日志记录。- 示例输出:
json
{"OtherInfo": {"SettingTimeStamp":"2024-5-20 PM 3:30:45.123456","SettingVersion":"1.0.0.0","SettingUnits":"℃"},"Values": [25.5, 26.1],"Strings": ["Temperature1", "Temperature2"] }
- 示例输出:
FromString(string info)
:从 JSON 字符串解析配置数据,自动填充iVal
、iStr
、otherInfo
,支持异常处理(解析失败返回false
)。
(4)序列化支持(ISerializable
)
实现 .NET 序列化接口,支持跨进程 / 跨平台数据传输,通过 GetObjectData
方法将核心字段(iVal
、iStr
、iArrayVal
、otherInfo
)写入序列化流,反序列化时通过构造函数恢复数据。
4. 配置容器类:SettingContainer
核心作用
管理多个 Setting
或嵌套 SettingContainer
,解决 “复杂配置场景”(如层级化系统设置、多设备参数集合)的存储和访问问题,相当于 “配置的字典容器”。
关键成员与能力
(1)核心存储结构
字段 | 类型 | 作用说明 |
---|---|---|
Settings | Dictionary<string, Setting> | 存储单一配置项(键 -Setting 映射) |
SettingsContainers | Dictionary<string, SettingContainer> | 存储嵌套容器(键 -SettingContainer 映射) |
delimiter | string (静态) | 键路径分隔符(默认 “.”),用于层级访问 |
(2)层级化键访问(核心亮点)
通过 索引器 this[string key]
支持 “点分隔符” 的层级化访问,无需手动逐层获取嵌套容器,极大简化代码。例如:
- 访问嵌套配置:
container["Device1.Temperature.SetPoint"]
,自动解析为container → SettingsContainers["Device1"] → SettingsContainers["Temperature"] → Settings["SetPoint"]
。 - 赋值嵌套配置:
container["Device1.Temperature.SetPoint"] = 25.0
,自动创建不存在的中间容器。
示例代码:
csharp
// 创建容器并添加嵌套配置
SettingContainer container = new SettingContainer();
// 直接赋值层级化配置(自动创建中间容器 Device1 和 Temperature)
container["Device1.Temperature.SetPoint"] = 25.0;
container["Device1.Temperature.Unit"] = "℃";// 直接访问层级化配置
double setPoint = (double)container["Device1.Temperature.SetPoint"];
string unit = (string)container["Device1.Temperature.Unit"];
Console.WriteLine($"SetPoint: {setPoint} {unit}"); // 输出 "SetPoint: 25 ℃"
(3)配置集合操作
提供丰富的集合级操作方法,满足批量管理需求:
Keys(string superKey = "")
:获取所有配置的键(含层级路径,如Device1.Temperature.SetPoint
)。GetSettingDictionary()
:将所有嵌套配置扁平化为 “键路径 -Setting
” 的字典,便于批量遍历。GetDictionary()
:将所有数值型配置扁平化为 “键路径 -double[]
” 的字典。updateSettings(Dictionary<string, double[]> dict)
:批量更新数值型配置。
(4)配置对比(实用功能)
compare(SettingContainer other, bool swap = true, double tolerance = 0.0)
方法:
- 对比当前容器与另一个容器的配置差异,返回差异字典(键路径 - 差异描述)。
- 支持数值容忍度(
tolerance
),避免浮点精度问题导致的误判。 - 支持忽略时间戳(
SettingTimeStamp
)和版本号(AcuShape.PROCESSING.version
)等动态字段。
(5)JSON 序列化与反序列化
重写 ToString(string level)
方法,将嵌套容器结构转换为层级化 JSON(含 SettingsContainers
和 Settings
节点),反序列化时通过 FromString2
方法递归解析嵌套结构。
四、核心设计亮点
1. 类型安全与灵活性平衡
- 通过隐式类型转换简化代码(如
double
直接转为Setting
),同时通过抽象类和接口约束确保类型安全。 - 支持
double
/string
/ 多维数组等多种数据类型,覆盖绝大多数配置场景。
2. 层级化配置管理
SettingContainer
的 “点分隔符” 索引器,解决嵌套配置的访问痛点,比传统字典嵌套更直观。
3. 序列化与可移植性
- 同时支持 JSON 字符串转换(便于日志和人工编辑)和 .NET 序列化(便于跨进程传输),满足不同场景的可移植需求。
4. 元数据管理
otherInfo
字典存储时间戳、版本、单位等元数据,让配置不仅包含 “数据”,还包含 “数据的描述信息”,便于追溯和维护。
五、典型应用场景
- 工业设备参数配置:存储 PLC、传感器的参数(如温度阈值、电机转速),通过
Setting
存储单设备参数,SettingContainer
管理多设备集合。 - 系统设置管理:存储软件的层级化设置(如
System.Network.IP
、System.Serial.BaudRate
),通过SettingContainer
实现路径化访问。 - 配置备份与恢复:通过
ToString()
将配置转为 JSON 字符串备份,恢复时通过FromString()
解析,支持人工编辑备份文件。 - 配置对比与同步:通过
SettingContainer.compare()
方法对比本地与远程设备的配置差异,实现增量同步。
六、使用注意事项
- 隐式转换的异常风险:隐式转换时会检查
Setting
实例和数据长度(如GenericSetting → double
要求Values
非空且长度≥1),需确保数据合法性,避免抛出Exception
。 - JSON 格式兼容性:
FromString()
方法依赖固定的 JSON 结构(含OtherInfo
、Values
、Strings
节点),自定义 JSON 时需遵循格式要求。 - 嵌套深度限制:
SettingContainer
的嵌套深度无硬限制,但过深的嵌套会影响性能和可读性,建议控制在 3-5 层以内。 - 线程安全:当前实现未包含线程安全控制,多线程并发访问时需手动添加锁(如
lock
),避免字典操作冲突。
七、总结
该配置数据管理体系通过 “接口 - 抽象类 - 具体类 - 容器类” 的层级设计,实现了 “单一配置项→复杂嵌套配置” 的全场景覆盖,兼具类型安全、灵活性、可移植性三大优势。无论是工业设备参数管理,还是软件系统设置,都能通过该体系实现标准化、可维护的配置管理,是一套设计精良的配置解决方案。