寫在開頭 今天就放假了,照理說應該寫今年的總結了,但是回頭一看,很久沒有寫過技術類的文字了,還是先不吐槽了。 關於文件緩存 寫了很多的代碼,常常在寫EXE(定時任務)或者寫小站點(數據的使用和客戶端調用之間)都需要用到緩存,數據在記憶體和文本都保留一個整體。 當然也可以寫到資料庫,不過個人覺得不方便查 ...
寫在開頭
今天就放假了,照理說應該寫今年的總結了,但是回頭一看,很久沒有寫過技術類的文字了,還是先不吐槽了。
關於文件緩存
寫了很多的代碼,常常在寫EXE(定時任務)或者寫小站點(數據的使用和客戶端調用之間)都需要用到緩存,數據在記憶體和文本都保留一個整體。
當然也可以寫到資料庫,不過個人覺得不方便查看和管理。(數據量不大)
第一個版本
一般來說,就是一個緩存類,然後存放緩存文件路徑,真實數據集合,讀寫集合,讀寫文件。
先提供一個公用類

1 public class TestInfo 2 { 3 public string Name { get; set; } 4 5 public int Age { get; set; } 6 7 public string Value { get; set; } 8 }測試公有類
然後是第一個對文件讀寫操作的緩存了

1 public class Cache_Test_1 2 { 3 private static List<TestInfo> dataList = new List<TestInfo>(); 4 5 private static readonly string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", "test.txt"); 6 7 private static readonly string SEP_STR = "---"; 8 9 static Cache_Test_1() 10 { 11 string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache"); 12 if (!Directory.Exists(dir)) 13 { 14 Directory.CreateDirectory(dir); 15 } 16 17 if (File.Exists(cachePath)) 18 { 19 string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8); 20 foreach (var line in lines) 21 { 22 string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None); 23 if (lineArray.Length == 3) 24 { 25 dataList.Add(new TestInfo() 26 { 27 Name = lineArray[0], 28 Age = int.Parse(lineArray[1]), 29 Value = lineArray[2] 30 }); 31 } 32 } 33 } 34 } 35 36 public static void AddInfo(TestInfo info) 37 { 38 lock (dataList) 39 { 40 var item = dataList.Find(p => p.Name == info.Name); 41 if (item == null) 42 { 43 dataList.Add(info); 44 } 45 else 46 { 47 item.Age = info.Age; 48 item.Value = info.Value; 49 } 50 51 WriteFile(); 52 } 53 } 54 55 public static TestInfo GetInfo(string name) 56 { 57 lock (dataList) 58 { 59 return dataList.Find(p => p.Name == name); 60 } 61 } 62 63 public static List<TestInfo> GetAll() 64 { 65 lock (dataList) 66 { 67 return dataList; 68 } 69 } 70 71 private static void WriteFile() 72 { 73 StringBuilder content = new StringBuilder(); 74 foreach (var item in dataList) 75 { 76 content.AppendLine(item.Name + SEP_STR + item.Age + SEP_STR + item.Value); 77 } 78 79 File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8); 80 } 81 }版本1,文件緩存,固定數據格式和類型
但是,這樣的操作如果多了起來,問題就出來了。每次寫一種緩存就要寫一個緩存操作的類了,寫著寫著,這樣的體力活就有點累了,於是
窮則思變,就想,寫一個通用一點的吧。於是又了第二個版本
第二個版本
這個版本的目的就是解決重覆勞動,見代碼

1 public class Cache_Test_2<T> where T : new() 2 { 3 /// <summary> 4 /// 緩存分隔符 5 /// </summary> 6 private static readonly string SEP_STR = "---"; 7 8 /// <summary> 9 /// 緩存臨時對象集合 10 /// </summary> 11 private static List<T> dataList = new List<T>(); 12 13 /// <summary> 14 /// 緩存文本路徑 15 /// </summary> 16 private static string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", typeof(T).Name.ToString() + ".txt"); 17 18 static Cache_Test_2() 19 { 20 string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache"); 21 if (!Directory.Exists(dir)) 22 { 23 Directory.CreateDirectory(dir); 24 } 25 26 if (File.Exists(cachePath)) 27 { 28 Type t = typeof(T); 29 string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8); 30 foreach (var line in lines) 31 { 32 string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None); 33 if (line.Contains(SEP_STR)) 34 { 35 List<PropertyIndexInfo> list = new List<PropertyIndexInfo>(); 36 T model = new T(); 37 PropertyInfo[] ps = t.GetProperties(); 38 for (int i = 0; i < lineArray.Length; i++) 39 { 40 var p = ps[i]; 41 if (p.PropertyType == typeof(int)) 42 { 43 p.SetValue(model, Convert.ToInt32(lineArray[i]), null); 44 } 45 else if (p.PropertyType == typeof(string)) 46 { 47 p.SetValue(model, lineArray[i], null); 48 } 49 } 50 51 dataList.Add(model); 52 } 53 } 54 } 55 } 56 57 /// <summary> 58 /// 新增一個緩存 59 /// </summary> 60 /// <param name="t"></param> 61 public static void Add(T t) 62 { 63 lock (dataList) 64 { 65 dataList.Add(t); 66 67 WriteFile(); 68 } 69 } 70 71 /// <summary> 72 /// 讀取緩存集合 73 /// </summary> 74 /// <returns></returns> 75 public static List<T> GetAll() 76 { 77 lock (dataList) 78 { 79 return dataList; 80 } 81 } 82 83 /// <summary> 84 /// 寫入緩存文件(全量) 85 /// </summary> 86 private static void WriteFile() 87 { 88 StringBuilder content = new StringBuilder(); 89 foreach (var item in dataList) 90 { 91 List<string> list = new List<string>(); 92 var ps = typeof(T).GetProperties(); 93 foreach (var p in ps) 94 { 95 object p_object = p.GetValue(item, null); 96 string value = p_object.ToString(); 97 list.Add(value); 98 } 99 100 content.AppendLine(string.Join(SEP_STR, list.ToArray())); 101 } 102 103 File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8); 104 } 105 }版本2,通用文件緩存(適合一般場景)
雖然,第二個版本出來了,但是大多數時候,我們創建緩存都是在已有的類上面進行操作,不然每次創建緩存可能就需要一個CacheModel這樣一個對象了,
這樣還有,並不是所有的欄位我們都是需要進入緩存文件,這樣的情況該如何操作呢,於是我們再次優化了一下代碼,出現了目前來說的第三個版本了。
第三個版本
這裡,就會新增幾個類了,為瞭解決第二個版本不能解決的問題,當然具體使用還是要看業務場景,因為,更通用就代表更依賴配置了。(代碼類)
這裡需要幾個基本類,用來保存臨時管理的。

1 /// <summary> 2 /// 特性:指定屬性的排序和是否出現在緩存中使用 3 /// </summary> 4 public class CacheOrderAttribute : Attribute 5 { 6 public int Index { get; set; } 7 } 8 9 /// <summary> 10 /// 對字元串分割的結果的值進行排序 11 /// </summary> 12 public class CacheIndexInfo 13 { 14 public int Index { get; set; } 15 16 public string Value { get; set; } 17 } 18 19 /// <summary> 20 /// 對欄位的屬性進行排序 21 /// </summary> 22 public class PropertyIndexInfo 23 { 24 public int Index { get; set; } 25 26 public PropertyInfo PropertyInfo { get; set; } 27 }特性和通用緩存用到的類
有了特性,我們就能對單個類的屬性進行特性篩查,排序,最終得到我們需要的和不需要的,最開始改好了,樓主測試了一下,速度上還可以,但是數據了一多,
這種每次全部寫入文件的方式就low了,於是改成了Append的方式,大大提升了速度。同時添加了一個類,對具體分割的值進行調整的。代碼如下:

1 /// <summary> 2 /// 文件緩存共有類 3 /// </summary> 4 /// <typeparam name="T">類</typeparam> 5 public class File_Common_Cache<T> where T : new() 6 { 7 /// <summary> 8 /// 緩存分隔符 9 /// </summary> 10 private static string SEP_STR = "---"; 11 12 /// <summary> 13 /// 緩存臨時對象集合 14 /// </summary> 15 private static List<T> dataList = new List<T>(); 16 17 /// <summary> 18 /// 緩存文本路徑 19 /// </summary> 20 private static string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", typeof(T).Name.ToString() + ".txt"); 21 22 static File_Common_Cache() 23 { 24 string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache"); 25 if (!Directory.Exists(dir)) 26 { 27 Directory.CreateDirectory(dir); 28 } 29 30 if (File.Exists(cachePath)) 31 { 32 Type t = typeof(T); 33 string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8); 34 foreach (var line in lines) 35 { 36 string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None); 37 if (line.Contains(SEP_STR)) 38 { 39 List<PropertyIndexInfo> list = new List<PropertyIndexInfo>(); 40 T model = new T(); 41 PropertyInfo[] ps = t.GetProperties(); 42 foreach (var p in ps) 43 { 44 var ads = p.GetCustomAttributesData(); 45 if (ads.Count > 0) 46 { 47 int index = Convert.ToInt32(ads[0].NamedArguments[0].TypedValue.Value); 48 list.Add(new PropertyIndexInfo() { Index = index, PropertyInfo = p }); 49 } 50 } 51 52 list = list.OrderBy(p => p.Index).ToList(); 53 for (int i = 0; i < list.Count; i++) 54 { 55 var pt = list[i].PropertyInfo.PropertyType; 56 if (pt == typeof(int)) 57 { 58 list[i].PropertyInfo.SetValue(model, Convert.ToInt32(lineArray[i]), null); 59 } 60 else if (pt == typeof(string)) 61 { 62 list[i].PropertyInfo.SetValue(model, lineArray[i], null); 63 } 64 else if (pt == typeof(DateTime)) 65 { 66 list[i].PropertyInfo.SetValue(model, Convert.ToDateTime(lineArray[i]), null); 67 } 68 else 69 { 70 try 71 { 72 list[i].PropertyInfo.SetValue(model, (object)lineArray[i], null); 73 } 74 catch 75 { 76 throw new Exception("不支持屬性類型(僅支持,int,string,DateTime,object)"); 77 } 78 } 79 } 80 81 dataList.Add(model); 82 } 83 } 84 } 85 } 86 87 /// <summary> 88 /// 初始化配置(修改預設分割和保存文件使用) 89 /// </summary> 90 /// <param name="sep_str">分隔符</param> 91 /// <param name="fileName">緩存文件名</param> 92 public static void InitSet(string sep_str, string fileName) 93 { 94 SEP_STR = sep_str; 95 cachePath = fileName; 96 } 97 98 /// <summary> 99 /// 新增一個緩存 100 /// </summary> 101 /// <param name="t"></param> 102 public static void Add(T t) 103 { 104 lock (dataList) 105 { 106 dataList.Add(t); 107 108 AppendFile(t); 109 } 110 } 111 112 /// <summary> 113 /// 移除一個緩存 114 /// </summary> 115 /// <param name="t"></param> 116 public static void Remove(T t) 117 { 118 119 } 120 121 /// <summary> 122 /// 讀取緩存集合 123 /// </summary> 124 /// <returns></returns> 125 public static List<T> GetAll() 126 { 127 lock (dataList) 128 { 129 return dataList; 130 } 131 } 132 133 /// <summary> 134 /// 寫入緩存文件(全量) 135 /// </summary> 136 private static void WriteFile() 137 { 138 StringBuilder content = new StringBuilder(); 139 foreach (var item in dataList) 140 { 141 List<CacheIndexInfo> list = new List<CacheIndexInfo>(); 142 var ps = typeof(T).GetProperties(); 143 foreach (var p in ps) 144 { 145 var ads = p.GetCustomAttributesData(); 146 if (ads.Count > 0) 147 { 148 int index = Convert.ToInt32(ads[0].NamedArguments[0].TypedValue.Value); 149 object p_object = p.GetValue(item, null); 150 string value = string.Empty; 151 if (p.PropertyType == typeof(DateTime)) 152 { 153 value = p_object == null ? DateTime.Parse("1900-1-1").ToString("yyyy-MM-dd HH:mm:ss") : 154 Convert.ToDateTime(p_object).ToString("yyyy-MM-dd HH:mm:ss"); 155 } 156 else 157 { 158 value = p_object == null ? "" : p_object.ToString(); 159 } 160 161 list.Add(new CacheIndexInfo() { Index = index, Value = value }); 162 } 163 } 164 165 list = list.OrderBy(a => a.Index).ToList(); 166 content.AppendLine(string.Join(SEP_STR, (from f in list select f.Value).ToArray())); 167 } 168 169 File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8); 170 } 171 172 /// <summary> 173 /// 寫入緩存文件(附加) 174 /// </summary> 175 /// <param name="t"></param> 176 private static void AppendFile(T t) 177 { 178 StringBuilder content = new StringBuilder(); 179 List<CacheIndexInfo> list = new List<CacheIndexInfo>(); 180 var ps = typeof(T).GetProperties(); 181 foreach (var p in ps) 182 { 183 var ads = p.GetCustomAttributesData(); 184 if (ads.Count > 0) 185 { 186 int index = Convert.ToInt32(ads[0].NamedArguments[0].TypedValue.Value); 187 object p_object = p.GetValue(t, null); 188 string value = string.Empty; 189 if (p.PropertyType == typeof(DateTime)) 190 { 191 value = p_object == null ? DateTime.Parse("1900-1-1").ToString("yyyy-MM-dd HH:mm:ss") : 192 Convert.ToDateTime(p_object).ToString("yyyy-MM-dd HH:mm:ss"); 193 } 194 else 195 { 196 value = p_object == null ? "" : p_object.ToString(); 197 } 198 199 list.Add(new CacheIndexInfo() { Index = index, Value = value }); 200