/// <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
),避免字典操作沖突。
七、總結
該配置數據管理體系通過 “接口 - 抽象類 - 具體類 - 容器類” 的層級設計,實現了 “單一配置項→復雜嵌套配置” 的全場景覆蓋,兼具類型安全、靈活性、可移植性三大優勢。無論是工業設備參數管理,還是軟件系統設置,都能通過該體系實現標準化、可維護的配置管理,是一套設計精良的配置解決方案。