• C#之文件缓存


    写在开头



      今天就放假了,照理说应该写今年的总结了,但是回头一看,很久没有写过技术类的文字了,还是先不吐槽了。


    关于文件缓存



      写了很多的代码,常常在写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                 }
    201             }
    202 
    203             list = list.OrderBy(a => a.Index).ToList();
    204             content.AppendLine(string.Join(SEP_STR, (from f in list select f.Value).ToArray()));
    205             File.AppendAllText(cachePath, content.ToString(), Encoding.UTF8);
    206         }
    207     }
    终版(文件缓存类)

    测试代码


     1 class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             TInfo info = new TInfo();
     6             info.Name = "test";
     7             info.Age = 10;
     8             info.Test = "我是测试字符串";
     9             var list = File_Common_Cache<TInfo>.GetAll();
    10             DateTime startTime = DateTime.Now;
    11             for (int i = 0; i < 1000; i++)
    12             {
    13                 File_Common_Cache<TInfo>.Add(info);
    14             }
    15 
    16             TimeSpan span = DateTime.Now - startTime;
    17             Console.WriteLine(span.TotalMilliseconds);
    18             Console.ReadLine();
    19         }
    20     }
    21 
    22     public class TInfo
    23     {
    24         [CacheOrder(Index = 0)]
    25         public string Name { get; set; }
    26 
    27         [CacheOrder(Index = 2)]
    28         public int Age { get; set; }
    29 
    30         [CacheOrder(Index = 1)]
    31         public string Test { get; set; }
    32     }
    测试代码

    测试结果:1秒不到,还是可以。

    总结


      没啥好总结的,但是不这样写,感觉不规范。写代码的过程中,总是会遇到,写着写着就变成体力活的代码,这个时候,我们就应该认识到问题了,把体力活改变一下,就不再是体力活。

    让代码更简单,让生活个多彩。

      不足之处,望包含,拍砖,丢鸡蛋。

     

  • 相关阅读:
    [HNOI2004]L语言
    快速沃尔什变换FWT
    [BZOJ1486][HNOI2009]最小圈
    [BZOJ4819][SDOI2017]新生舞会
    [POJ2976]Dropping tests
    CTSC2018&APIO2018游记
    [Luogu3769][CH弱省胡策R2]TATT
    [BZOJ3489]A simple rmq problem
    [BZOJ4066]简单题
    [BZOJ2648]SJY摆棋子
  • 原文地址:https://www.cnblogs.com/Supperlitt/p/8444629.html
Copyright © 2020-2023  润新知