写在开头


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

关于文件缓存


  写了很多的代码,常常在写EXE(定时任务)或者写小站点(数据的使用和客户端调用之间)都需要用到缓存,数据在内存和文本都保留一个整体。

当然也可以写到数据库,不过个人觉得不方便查看和管理。(数据量不大)

第一个版本


  一般来说,就是一个缓存类,然后存放缓存文件路径,真实数据集合,读写集合,读写文件。

  先提供一个公用类

 public class TestInfo
{
public string Name { get; set; } public int Age { get; set; } public string Value { get; set; }
}

测试公有类

  然后是第一个对文件读写操作的缓存了

 public class Cache_Test_1
{
private static List<TestInfo> dataList = new List<TestInfo>(); private static readonly string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", "test.txt"); private static readonly string SEP_STR = "---"; static Cache_Test_1()
{
string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
} if (File.Exists(cachePath))
{
string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
foreach (var line in lines)
{
string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
if (lineArray.Length == )
{
dataList.Add(new TestInfo()
{
Name = lineArray[],
Age = int.Parse(lineArray[]),
Value = lineArray[]
});
}
}
}
} public static void AddInfo(TestInfo info)
{
lock (dataList)
{
var item = dataList.Find(p => p.Name == info.Name);
if (item == null)
{
dataList.Add(info);
}
else
{
item.Age = info.Age;
item.Value = info.Value;
} WriteFile();
}
} public static TestInfo GetInfo(string name)
{
lock (dataList)
{
return dataList.Find(p => p.Name == name);
}
} public static List<TestInfo> GetAll()
{
lock (dataList)
{
return dataList;
}
} private static void WriteFile()
{
StringBuilder content = new StringBuilder();
foreach (var item in dataList)
{
content.AppendLine(item.Name + SEP_STR + item.Age + SEP_STR + item.Value);
} File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
}
}

版本1,文件缓存,固定数据格式和类型

  但是,这样的操作如果多了起来,问题就出来了。每次写一种缓存就要写一个缓存操作的类了,写着写着,这样的体力活就有点累了,于是

穷则思变,就想,写一个通用一点的吧。于是又了第二个版本

第二个版本


  这个版本的目的就是解决重复劳动,见代码

 public class Cache_Test_2<T> where T : new()
{
/// <summary>
/// 缓存分隔符
/// </summary>
private static readonly string SEP_STR = "---"; /// <summary>
/// 缓存临时对象集合
/// </summary>
private static List<T> dataList = new List<T>(); /// <summary>
/// 缓存文本路径
/// </summary>
private static string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", typeof(T).Name.ToString() + ".txt"); static Cache_Test_2()
{
string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
} if (File.Exists(cachePath))
{
Type t = typeof(T);
string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
foreach (var line in lines)
{
string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
if (line.Contains(SEP_STR))
{
List<PropertyIndexInfo> list = new List<PropertyIndexInfo>();
T model = new T();
PropertyInfo[] ps = t.GetProperties();
for (int i = ; i < lineArray.Length; i++)
{
var p = ps[i];
if (p.PropertyType == typeof(int))
{
p.SetValue(model, Convert.ToInt32(lineArray[i]), null);
}
else if (p.PropertyType == typeof(string))
{
p.SetValue(model, lineArray[i], null);
}
} dataList.Add(model);
}
}
}
} /// <summary>
/// 新增一个缓存
/// </summary>
/// <param name="t"></param>
public static void Add(T t)
{
lock (dataList)
{
dataList.Add(t); WriteFile();
}
} /// <summary>
/// 读取缓存集合
/// </summary>
/// <returns></returns>
public static List<T> GetAll()
{
lock (dataList)
{
return dataList;
}
} /// <summary>
/// 写入缓存文件(全量)
/// </summary>
private static void WriteFile()
{
StringBuilder content = new StringBuilder();
foreach (var item in dataList)
{
List<string> list = new List<string>();
var ps = typeof(T).GetProperties();
foreach (var p in ps)
{
object p_object = p.GetValue(item, null);
string value = p_object.ToString();
list.Add(value);
} content.AppendLine(string.Join(SEP_STR, list.ToArray()));
} File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
}
}

版本2,通用文件缓存(适合一般场景)

  虽然,第二个版本出来了,但是大多数时候,我们创建缓存都是在已有的类上面进行操作,不然每次创建缓存可能就需要一个CacheModel这样一个对象了,

这样还有,并不是所有的字段我们都是需要进入缓存文件,这样的情况该如何操作呢,于是我们再次优化了一下代码,出现了目前来说的第三个版本了。

第三个版本


  这里,就会新增几个类了,为了解决第二个版本不能解决的问题,当然具体使用还是要看业务场景,因为,更通用就代表更依赖配置了。(代码类)

  这里需要几个基本类,用来保存临时管理的。

 

     /// <summary>
/// 特性:指定属性的排序和是否出现在缓存中使用
/// </summary>
public class CacheOrderAttribute : Attribute
{
public int Index { get; set; }
} /// <summary>
/// 对字符串分割的结果的值进行排序
/// </summary>
public class CacheIndexInfo
{
public int Index { get; set; } public string Value { get; set; }
} /// <summary>
/// 对字段的属性进行排序
/// </summary>
public class PropertyIndexInfo
{
public int Index { get; set; } public PropertyInfo PropertyInfo { get; set; }
}

特性和通用缓存用到的类

  有了特性,我们就能对单个类的属性进行特性筛查,排序,最终得到我们需要的和不需要的,最开始改好了,楼主测试了一下,速度上还可以,但是数据了一多,

这种每次全部写入文件的方式就low了,于是改成了Append的方式,大大提升了速度。同时添加了一个类,对具体分割的值进行调整的。代码如下:

 /// <summary>
/// 文件缓存共有类
/// </summary>
/// <typeparam name="T">类</typeparam>
public class File_Common_Cache<T> where T : new()
{
/// <summary>
/// 缓存分隔符
/// </summary>
private static string SEP_STR = "---"; /// <summary>
/// 缓存临时对象集合
/// </summary>
private static List<T> dataList = new List<T>(); /// <summary>
/// 缓存文本路径
/// </summary>
private static string cachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache", typeof(T).Name.ToString() + ".txt"); static File_Common_Cache()
{
string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "cache");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
} if (File.Exists(cachePath))
{
Type t = typeof(T);
string[] lines = File.ReadAllLines(cachePath, Encoding.UTF8);
foreach (var line in lines)
{
string[] lineArray = line.Split(new string[] { SEP_STR }, StringSplitOptions.None);
if (line.Contains(SEP_STR))
{
List<PropertyIndexInfo> list = new List<PropertyIndexInfo>();
T model = new T();
PropertyInfo[] ps = t.GetProperties();
foreach (var p in ps)
{
var ads = p.GetCustomAttributesData();
if (ads.Count > )
{
int index = Convert.ToInt32(ads[].NamedArguments[].TypedValue.Value);
list.Add(new PropertyIndexInfo() { Index = index, PropertyInfo = p });
}
} list = list.OrderBy(p => p.Index).ToList();
for (int i = ; i < list.Count; i++)
{
var pt = list[i].PropertyInfo.PropertyType;
if (pt == typeof(int))
{
list[i].PropertyInfo.SetValue(model, Convert.ToInt32(lineArray[i]), null);
}
else if (pt == typeof(string))
{
list[i].PropertyInfo.SetValue(model, lineArray[i], null);
}
else if (pt == typeof(DateTime))
{
list[i].PropertyInfo.SetValue(model, Convert.ToDateTime(lineArray[i]), null);
}
else
{
try
{
list[i].PropertyInfo.SetValue(model, (object)lineArray[i], null);
}
catch
{
throw new Exception("不支持属性类型(仅支持,int,string,DateTime,object)");
}
}
} dataList.Add(model);
}
}
}
} /// <summary>
/// 初始化配置(修改默认分割和保存文件使用)
/// </summary>
/// <param name="sep_str">分隔符</param>
/// <param name="fileName">缓存文件名</param>
public static void InitSet(string sep_str, string fileName)
{
SEP_STR = sep_str;
cachePath = fileName;
} /// <summary>
/// 新增一个缓存
/// </summary>
/// <param name="t"></param>
public static void Add(T t)
{
lock (dataList)
{
dataList.Add(t); AppendFile(t);
}
} /// <summary>
/// 移除一个缓存
/// </summary>
/// <param name="t"></param>
public static void Remove(T t)
{ } /// <summary>
/// 读取缓存集合
/// </summary>
/// <returns></returns>
public static List<T> GetAll()
{
lock (dataList)
{
return dataList;
}
} /// <summary>
/// 写入缓存文件(全量)
/// </summary>
private static void WriteFile()
{
StringBuilder content = new StringBuilder();
foreach (var item in dataList)
{
List<CacheIndexInfo> list = new List<CacheIndexInfo>();
var ps = typeof(T).GetProperties();
foreach (var p in ps)
{
var ads = p.GetCustomAttributesData();
if (ads.Count > )
{
int index = Convert.ToInt32(ads[].NamedArguments[].TypedValue.Value);
object p_object = p.GetValue(item, null);
string value = string.Empty;
if (p.PropertyType == typeof(DateTime))
{
value = p_object == null ? DateTime.Parse("1900-1-1").ToString("yyyy-MM-dd HH:mm:ss") :
Convert.ToDateTime(p_object).ToString("yyyy-MM-dd HH:mm:ss");
}
else
{
value = p_object == null ? "" : p_object.ToString();
} list.Add(new CacheIndexInfo() { Index = index, Value = value });
}
} list = list.OrderBy(a => a.Index).ToList();
content.AppendLine(string.Join(SEP_STR, (from f in list select f.Value).ToArray()));
} File.WriteAllText(cachePath, content.ToString(), Encoding.UTF8);
} /// <summary>
/// 写入缓存文件(附加)
/// </summary>
/// <param name="t"></param>
private static void AppendFile(T t)
{
StringBuilder content = new StringBuilder();
List<CacheIndexInfo> list = new List<CacheIndexInfo>();
var ps = typeof(T).GetProperties();
foreach (var p in ps)
{
var ads = p.GetCustomAttributesData();
if (ads.Count > )
{
int index = Convert.ToInt32(ads[].NamedArguments[].TypedValue.Value);
object p_object = p.GetValue(t, null);
string value = string.Empty;
if (p.PropertyType == typeof(DateTime))
{
value = p_object == null ? DateTime.Parse("1900-1-1").ToString("yyyy-MM-dd HH:mm:ss") :
Convert.ToDateTime(p_object).ToString("yyyy-MM-dd HH:mm:ss");
}
else
{
value = p_object == null ? "" : p_object.ToString();
} list.Add(new CacheIndexInfo() { Index = index, Value = value });
}
} list = list.OrderBy(a => a.Index).ToList();
content.AppendLine(string.Join(SEP_STR, (from f in list select f.Value).ToArray()));
File.AppendAllText(cachePath, content.ToString(), Encoding.UTF8);
}
}

终版(文件缓存类)

测试代码


 class Program
{
static void Main(string[] args)
{
TInfo info = new TInfo();
info.Name = "test";
info.Age = ;
info.Test = "我是测试字符串";
var list = File_Common_Cache<TInfo>.GetAll();
DateTime startTime = DateTime.Now;
for (int i = ; i < ; i++)
{
File_Common_Cache<TInfo>.Add(info);
} TimeSpan span = DateTime.Now - startTime;
Console.WriteLine(span.TotalMilliseconds);
Console.ReadLine();
}
} public class TInfo
{
[CacheOrder(Index = )]
public string Name { get; set; } [CacheOrder(Index = )]
public int Age { get; set; } [CacheOrder(Index = )]
public string Test { get; set; }
}

测试代码

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

总结


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

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

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

最新文章

  1. 69个经典Spring面试题和答案
  2. Docker私服仓库push 错误
  3. Y+的查看及FLUENT壁面函数的选择
  4. ahjesus配置vsftpd虚拟用户在Ubuntu
  5. bookstrap必备的基础知识
  6. Windows Phone 7 播放视频
  7. 【Origin】羡旁人
  8. 【转】用 SVN Importer 实现 CSVNT 到 SVN 的转换
  9. 剑指OFFER之旋转数组的最小数字(九度OJ1386)
  10. 在VHDL中,&ldquo;传输延迟&rdquo;和&ldquo;惯性延迟&rdquo;
  11. python开发_大小写转换,首字母大写,去除特殊字符
  12. 常用Android开源框架
  13. 汉高澳大利亚sinox接口捆绑经典winxp,全面支持unicode跨语言处理
  14. 继续推荐几款VisualStudio的插件
  15. ASP.NET Core MVC之Serilog日志处理,你了解多少?
  16. NGUI_01
  17. cookie 和session 详解
  18. python基础之 面向对象之反射
  19. 珍藏的数据库SQL基础练习题答案
  20. NYOJ-------三角形

热门文章

  1. Dockerfile中CMD和ENTRYPOINT的区别
  2. cobol
  3. virtuoso装载大的rdf文件的方法
  4. struts文件异常Included file cannot be found
  5. 怎样用PS对照片进行美白?
  6. [知了堂学习笔记]_ajax的两种使用方式
  7. Pandas(python)数据处理:只对某一列DataFrame数据进行归一化
  8. c# gdi设置画刷透明
  9. elasticsearch安装ik分词器
  10. nginx添加编译lua模块