没有做到异步响应,一旦查找文件数量增多会导致界面停止响应,于是我们为其增加异步响应代码及相关的事件处理:

void logs(string msg)
        {
            tbxLog.AppendText(msg + "\r\n");
        }

        void tip(string msg)
        {
            lblTip.Text = msg;
        }

        void file_found(FileInfo fi)
        {
            if (fi.FullName.Contains(find_str))
            {
                logs(fi.FullName);
            }
            tip(fi.FullName);
            file_count++;
        }

        void folder_found(DirectoryInfo di)
        {
            tip(di.FullName);
            file_count++;
        }

        void search_begin()
        {
            logs("开始");
            file_count = 0;
        }

        void search_end()
        {
            logs(string.Format("结束。检索文件数量:{0}", file_count));
        }

        void search_exception(Exception ex)
        {
            logs(ex.Message);
        }

        int file_count = 0;
        string find_str = "";
        string file_ext = "";

        async void CallSearch(string root_folder)
        {
            DirectoryInfo di = new DirectoryInfo(root_folder);
            search_begin();
            await SearchAllFiles(di);
            search_end();
        }

        async Task SearchAllFiles(DirectoryInfo di)
        {
            try
            {
                folder_found(di);
                FileInfo[] files = di.GetFiles(file_ext);
                foreach (FileInfo fi in files)
                {
                    file_found(fi);
                }
                await Task.Delay(1);
                DirectoryInfo[] direct = di.GetDirectories();
                foreach (DirectoryInfo diTemp in direct)
                {
                    await SearchAllFiles(diTemp);
                }
            }
            catch (Exception ex)
            {
                search_exception(ex);
            }
        }

        private void btnLevel1_Click(object sender, EventArgs e)
        {
            string root_folder = "C:\\test";
            file_ext = "*.xls";
            find_str = "预算";
            CallSearch(root_folder);
        }

相比上一节,这段代码的主要改进是:

引入异步调用

引入了异步调用,界面不再死锁了。调用流程就是主界面异步调用CallSearch,CallSearch中await SearchAllFiles(递归),直到SearchAllFiles处理完成。相比以前的多线程Thread,用Task+异步的方式,感觉更灵活一些,因为它有很多关键字和方法可供使用,多线程方式很多都要自己写。关于异步编程,以后可以专门推出一个系列来讲,这里就点到为止吧。

增加事件处理机制

我在这里定义了5种事件:开始查找search_begin、找到目录folder_found、找到文件file_found、查找结束search_end、处理异常search_exception。然后分别在不同的场合下调用。

界面运行如下:

ba077b539890b5e133b7b295eedf6d56.png

应该说,相比上一个版本,有了较大的改进,这个水平基本能够满足正常的交付要求了。我姑且把这个层级称为入门级,毕竟有了事件的概念,但还不具备事件系统的思维,距离优雅则更是差了几个层级。作为一名程序员,程序能够满足功能只是最基本的要求,想要以后的工作省力,就一定要考虑代码复用性的问题,因为用户需求是不断变化的,如何让你的代码以最小的变化(甚至不变)去适应最多可能的变化,这才是程序员所追求的匠人精神,才是我眼中的优雅。在我以往的文章中,我发现有不少人着眼于代码风格、代码量、语法糖等等,那些其实都无足轻重,对你的代码积累没有实质性的影响,其实只要一出口段位就Low了。顺便再提一句,C#的效率还是相当高的,51个文件的查找几乎就是秒完。

下面我就举两个例子,来说明下上面代码的局限性:

现在有另一个功能是需要读取里面内容并进行替换,那么file_found部分的代码就需要变了,所以不够优雅;

当然你可以说多大点事,Ctrl+C、Ctrl+V,修修改改,几分钟搞定。好没问题,那我们现在需要专门做一个文件批处理的工具软件,可能有不下100种文件的处理方式,这时候你还是Ctrl+C、Ctrl+V吗?能累死你否?当然也许你也像我一样的实用主义以及像小蜜蜂一样的不辞辛劳,老子不在乎,再说辛苦也就这一次,改完就好了,反正100种处理方式也都得分别做改动。

好吧,你赢了。那接下来产品马上要上线了测试发现,之前写的代码有Bug,另外还需要增加查找过程中的取消、暂停、恢复等功能,这100个功能都得改,另外要的比较急,今天务必交付给测试。你还做得到吗?以后要是再有新增功能或Bug呢?

当然我倒不是说任何代码都得朝这个方向去进化,我只是想表达作为一名程序员,首先要具备这样的设计能力,至于具体要不要这样设计完全取决于你,主动权在你,万一你就是想图老板的加班费呢:)

于是乎,我们还需要向更高一个层级进军。