博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
小工具?不,这是小工具的集合!
阅读量:6256 次
发布时间:2019-06-22

本文共 10237 字,大约阅读时间需要 34 分钟。

ok,好像很久很久没有写博客了,

emmmm,一个是没时间,另一个,应该是感觉自己知道的太少了吧,不敢写了

原本是打算写一个系列,结果发现很多地方自己都是有些不够的,所以就一直放着了,

这次趁着国庆,补上一篇吧,算是一个小工具的实例,文末会提供源码下载

 

就是不知道大佬们有没有遇到过这种情况啊,

可能有时候要批量处理一些东西,可能是文件,可能是数据,总之就是处理量非常大,

正常人吧,要嘛是分发给很多人处理,要嘛就是一个人哭唧唧的弄上好几天,

容易出现错漏不说,一旦要求改了,ok,重新来过吧,

我们程序员就不一样了,不会偷懒的程序员不是一个好的死肥宅,

噼里啪啦写好一个小工具,然后就可以喝茶了,

待处理的文件或者数据,无论是一千还是一万,对我们来说只是一个数字而已,

哪怕你要求改了,ok,我工具改一下,同样可以施施然的跑去休息。

 

我应为工作原因,经常写一些工具代码,

看同事写工具代码的话,他们一般都是新建一个控制台程序,

然后要么写类,要么写方法,在Main中调用就好,

这个时候,问题就来了:

可能一个Main里面,全是被注释的其他工具调用代码,时间长了谁也不知道这些是干啥的,

用方法去区分工具的话,一个工具往往可能衍生出多个方法,调用的寻找都极不方便,

新建控制台项目的话,花销太大,很多方法可以重复使用,复制来复制去也是相当难以管理,

用类区分倒是不错,不过,调用起来也是麻烦,得先去Main中注释掉其他工具,只留下待执行的工具,

如果要执行多个工具,还得停下来去修改Main,体验感同样极不友好,

比如说这样,上面的代码全是以前写的工具,真正要执行的是81行的方法,

可以想象,长此以往,这里谁看到了都要头疼,

 

为了方便自己,所以抽空做了一个小项目,用于管理这些小工具,看图说话,

 

 

先说说思路吧,倒是蛮简单,就是反射,

先定义一个父类BaseFun,所有封装的小工具类都继承它,

1         public class TestClass : BaseFun2         {3 4         }

另外有三个参数公用

1         ///  2         /// 获取当前程序集 3         ///  4         Assembly _assembly = Assembly.GetExecutingAssembly(); 5  6         ///  7         /// 当前选择的类,此处不应这样写,仅作参考 8         ///  9         Type selType = _assembly.GetType(cmb_Class.SelectedValue.ToString());10 11         /// 12         /// 当前选择的方法,此处不应这样写,仅作参考13         /// 14         MethodInfo selMethod = selType.GetMethod(cmb_Fun.SelectedValue.ToString());

然后通过反射找到所有父类是BaseFun的类,加载至第一个下拉框里面,

这里简单用到了反射和委托,不熟的童鞋可以多瞅几遍,大佬勿喷,

1         ///  2         /// 窗体加载时执行 3         ///  4         ///  5         ///  6         private void F_Main_Load(object sender, EventArgs e) 7         {
11 // 绑定类的信息12 BindCom(13 cmb_Class,// 待绑定的下拉框14 _assembly.GetTypes(),// 获取程序集中所有的类15 c => c.BaseType == typeof(BaseFun),// 父类是BaseFun16 c => new ComBoxItem() { Display = c.Name, Value = c.FullName });17 }18 19 /// 20 /// 绑定下拉框选项21 /// 22 ///
23 /// 待绑定的下拉框24 /// 数据集25 /// 过滤条件,委托26 /// 返回下拉项,委托27 public void BindCom
(ComboBox cmb, ICollection
dataList, Func
funcWhere, Func
func)28 {29 List
list = new List
();30 31 if (!dataList.HasItems()) return;32 33 // 循环数据集34 foreach (var item in dataList)35 {36 // 执行条件37 if (funcWhere.Invoke(item))38 list.Add(func.Invoke(item));39 }40 41 if (!list.HasItems()) return;42 43 // 绑定数据集44 ComBoxItem option = list[0];45 cmb.ValueMember = nameof(option.Value);46 cmb.DisplayMember = nameof(option.Display);47 cmb.DataSource = list;48 }49 50 ///
51 /// 下拉框选项52 /// 53 public class ComBoxItem54 {55 ///
56 /// 值57 /// 58 public string Value { get; set; }59 ///
60 /// 文本61 /// 62 public string Display { get; set; }63 }

然后就是去找每个类里面的方法了,这里我考虑到可能会要求弹框提示一下运行结束,或者展示一些运行信息什么的,

所以就很果断的限定了返回值,只有当返回值为Result类型的时候,它才会去绑定到第二个下拉框中,话说这个Result类的命名好像不太好,啧,再说吧

1         ///  2         /// 选项更改时执行,重新绑定方法列表 3         ///  4         ///  5         ///  6         private void cmb_Class_SelectedIndexChanged(object sender, EventArgs e) 7         {
9 // 获取当前选择的类10 selType = _assembly.GetType(cmb_Class.SelectedValue.ToString());11 12 if (selType == null) return;13 14 // 绑定方法的信息15 BindCom(16 cmb_Fun,17 selType.GetMethods(),// 返回类中所有公开方法18 c => c.ReturnType == typeof(Result) && c.DeclaringType == selType,// 返回类型为Result,且是由当前类定义,而不是继承自父类的方法19 c => new ComBoxItem() { Display = c.Name, Value = c.Name });20 }21 /// 22 /// 返回结果23 /// 24 public class Result25 {26 /// 27 /// 消息28 /// 29 public string Msg { get; set; }30 31 /// 32 /// 运行时间,ms33 /// 34 public long RunTime { get; set; }35 36 }

找到了方法之后,就应该开始绑定参数了,

1         ///  2         /// 选项更改时执行,重新绑定参数列表 3         ///  4         ///  5         ///  6         private void cmb_Fun_SelectedIndexChanged(object sender, EventArgs e) 7         { 8             selMethod = selType.GetMethod(cmb_Fun.SelectedValue.ToString()); 9             BindPara();10         }11         /// 12         /// 绑定参数列表13         /// 14         private void BindPara()15         {16             // 清空所有控件17             flp_Para.Controls.Clear();18 19             string name = $"M:{selType.FullName}.{selMethod.Name}";20 21             int y = 5;22 23             if (selMethod.GetParameters().Length > 0)// 拼接寻找方法注释的name属性值24                 name += $"({string.Join(",", selMethod.GetParameters().Select(c => c.ParameterType.FullName))})";25 26             // 循环方法所需的所有参数27             foreach (var item in selMethod.GetParameters())28             {29                 int x = 0;30 31                 // 加载参数32                 SkinLabel paraName = new SkinLabel33                 {34                     Location = new Point(x, y + 2),35                     TextAlign = ContentAlignment.MiddleRight,36                     Size = new Size(80, 20),37                     Text = item.Name + ":"38                 };39                 paraName.MouseMove += Form_MouseDown;40 41                 x += paraName.Size.Width + 5;42 43                 // 加载文本框44                 SkinTextBox text = new SkinTextBox45                 {46                     Name = item.Name,47                     Size = new Size(150, 20),48                     Location = new Point(x, y),49                     WaterText = GetNote(name, item.Name)// 添加水印注释50                 };51 52                 x += text.Size.Width + 5;53 54                 // 加载参数类型55                 SkinLabel paraType = new SkinLabel56                 {57                     Location = new Point(x, y + 2),58                     TextAlign = ContentAlignment.MiddleLeft,59                     Size = new Size(70, 20),60                     Text = item.ParameterType.Name61                 };62                 paraType.MouseMove += Form_MouseDown;63 64                 y += 27;65 66                 flp_Para.Controls.Add(paraName);67                 flp_Para.Controls.Add(text);68                 flp_Para.Controls.Add(paraType);69             }70         }

考虑到可读性,所以我把参数的注释也找了出来,绑定到文本框的水印中去了,

Winform自带的文本框是没有水印这个功能的,所以我用了第三方的水印控件CSkin,

那么,这时候肯定有人问了,C#代码的注释怎么整,

代码编译后的Dll里面是没有注释的,所以反射也找不到注释,总不能去读.cs文件吧,

其实简单设置一下,VS就会自动帮我们生成一份注释文档,

最后在bin\Debug目录下,就会有一个XML注释文档,我们直接读取它就可以了,所有类和方法的节点都是member,写好寻找代码就可以了

///         /// 返回注释信息        ///         /// 名称        /// 参数        /// 
private string GetNote(string name, string para) { // 读取XML XDocument document = XDocument.Load(_assembly.GetName().Name + ".xml"); // 根据name寻找节点 var item = document.Descendants("member").Where(c => c.Attribute("name").Value == name).FirstOrDefault(); if (item == null) return ""; // 若参数名称为空 if (string.IsNullOrWhiteSpace(para)) return (item.Element("summary")?.Value + "").Replace("\n", "").Trim(); // 返回参数注释 return (item.Elements("param").Where(c => c.Attribute("name").Value == para).FirstOrDefault()?.Value + "").Replace("\n", "").Trim(); }

到这基本方法都能找对了,参数也能加载出来,接下来就是执行方法了,

1         ///  2         /// 按钮单击时执行,执行选中的指定方法 3         ///  4         ///  5         ///  6         private void bt_Exec_Click(object sender, EventArgs e) 7         { 8             List list = new List(); 9 10             // 循环方法的所有参数11             foreach (var item in selMethod.GetParameters())12             {13                 // 寻找和参数名称相同的控件14                 Control con = flp_Para.Controls.Find(item.Name, false).FirstOrDefault();15                 object obj = null;16 17                 #region 参数校验18 19                 if (con == null)20                 {21                     MessageBox.Show($"缺少参数:{item.Name}");22                     return;23                 }24                 if (string.IsNullOrWhiteSpace(con.Text))25                 {26                     MessageBox.Show($"{item.Name}:值为空");27                     return;28                 }29 30                 try31                 {32                     obj = Convert.ChangeType(con.Text, item.ParameterType);33                 }34                 catch (Exception)35                 {36                     MessageBox.Show($"{item.Name}:类型错误 ({item.ParameterType.Name})");37                     return;38                 }39 40                 #endregion41 42                 list.Add(obj);43 44             }45 46             Result res = (Result)selMethod.Invoke(_assembly.CreateInstance(selType.FullName), list.ToArray());47 48             if (cb_Log.Checked)49             {50                 res.Msg += $"\n{selType.Name}\t{selMethod.Name}\t运行时间:{res.RunTime} ms\n";51                 MessageBox.Show(res.Msg);52             }53         }

最后要注意的是写这些工具方法入口的规则,只要返回值为Resule,就能找到,加载,然后运行,

但同时我也提供了一个更好的入口,

内部更多的实现就不展示了,我会提供源码下载地址,大概思路便是如此,

public Result Fun1()        {            // 推荐写法,自动计算方法运行时间,自动拼装日志路径,自动记录每一次的执行            // logPath:日志文件路径            return RunFun((logPath) =>            {                                // 写入日志文件                base.WriteLog(logPath, "lalal");                // 方法运行结束后,在弹出的对话框中展示                Res.Msg += logPath;                return Res;            });        }        ///         /// 运行        ///         ///         /// 
public Result RunFun(Func
func) { Res = new Result(); // 拼装日志文件路径 string logPath = LogStarPath + GetMethodName(2) + ".log"; WriteLog(logPath, "==========Star=========="); // 定时器 Stopwatch watch = new Stopwatch(); // 开始计时 watch.Start(); // 执行方法 func.Invoke(logPath); // 停止计时 watch.Stop(); // 返回运行时间 Res.RunTime = watch.ElapsedMilliseconds; WriteLog(logPath, "==========End ==========\t" + Res.RunTime + " ms\n"); return Res; }

 

还有很多想做的啊,

比如说默认值这个玩意儿我不知道应该如何绑定到文本框里,

现在还没做链接数据库,

我还想记录每一次运行的参数,弄个下拉框,选一下就可以直接绑定一起曾经输入过的参数,这样也是很方便的,

以后再慢慢加吧,欢迎大佬们指出不足之处,

 

 码云地址:

 

转载于:https://www.cnblogs.com/Onlooker/p/9740016.html

你可能感兴趣的文章
走红日本 阿里云如何能够赢得海外荣耀
查看>>
qt 学习之路2
查看>>
线上应用故障排查之二:高内存占用
查看>>
异常处理汇总 ~ 修正果带着你的Code飞奔吧!
查看>>
PCIE_DMA:xapp1052学习笔记
查看>>
python ----字符串基础练习题30道
查看>>
uva-10879-因数分解
查看>>
python 调用aiohttp
查看>>
跨域iframe高度自适应(兼容IE/FF/OP/Chrome)
查看>>
学习鸟哥的Linux私房菜笔记(8)——文件查找与文件管理2
查看>>
升级fedora 18到fedora 19
查看>>
11月20日学习内容整理:jquery插件
查看>>
SVN与TortoiseSVN实战:补丁详解
查看>>
获取页面中所有dropdownlist类型控件
查看>>
读《淘宝数据魔方技术架构解析》有感
查看>>
[转载]如何破解Excel VBA密码
查看>>
【BZOJ】2563: 阿狸和桃子的游戏
查看>>
redis 中文字符显示
查看>>
顺序图【6】--☆☆
查看>>
Docker Swarm 让你事半功倍
查看>>