天天看點

C# 基礎知識系列- 17 小工具優化

C# 基礎知識系列- 17 小工具優化

  1. 前言

    不知道有沒有動手能力強的小夥伴照着上一篇的内容寫過程式呢?如果有的話,應該會在使用的時候發現以下幾個問題:

每次啟動都需要經過漫長的時間去周遊磁盤裡的檔案目錄

因為資料是用的字典儲存的,是以會消耗大量的記憶體空間

不能多次查詢

現在我們就針對這些問題,讓我們的小工具實用起來。

  1. 分析與實作

    在動手之前,我們先分析一下問題。在實際開發之前,無論是接到什麼需求都要先仔細分析一下,确定好方案再動手方為開發的正道。嗯,沒毛病。因為開發過程中跟産品對線、跟客戶對線要占整個項目的一半左右時間。好了,不廢話了。繼續:

周遊檔案目錄的時間過長,那麼我們是不是可以用異步并發去周遊呢?

資料用字典儲存會消耗記憶體空間,那麼我們是不是可以用其他的方式儲存呢?

不能多次查詢,是不是可以使用循環,然後設定一個退出條件?

1.1 C#的異步/并發實作

在C#裡,異步和并發的實作是依據線程、任務來實作的。在之前《C# 基礎知識系列- 12 任務和多線程》裡大概介紹了一下線程和任務,我們知道線程本身是沒法傳回資料的,它與主線程進行資料互動的過程十分需要注意線程安全。而任務可以傳回資料,不需要像線程一樣小心翼翼地與主線程進行資料互動。任務有一個優點,它比線程更輕量,是以在目前環境下我們可以試試任務。

當然,線程也有優點,那就是線程的運作環境相對更封閉一點,它能完成一個長的大型運算。

那麼繼續上一篇的内容,先引用 :

using System.Threading.Tasks;

先提取一組根據可枚舉目錄集合建立任務組并取得結果的方法:

public static Dictionary> OverDirectories(IEnumerable directories)

{

var tasks = directories.Select(dir => Task.Run(()=>OverDirectories(dir))).ToArray();
Task.WaitAll(tasks);// 這行的意思是等待所有任務完成
return Concat(tasks.Select(t=>t.Result).ToArray());           

}

然後改造原有的OverDirectories方法:

public static Dictionary> OverDirectories(DirectoryInfo rootDirectory)

Console.WriteLine($"正在周遊目錄:{rootDirectory.FullName}");
var dict = new Dictionary<string, List<string>>();
IEnumerable<FileInfo> files = new List<FileInfo>();
try
{
    files = rootDirectory.EnumerateFiles();
}
catch(Exception e)
{
    Console.WriteLine($"錯誤資訊:{e}");//列印錯誤資訊
}

foreach(var file in files)
{
    var key = Path.GetFileNameWithoutExtension(file.Name);
    if(!dict.ContainsKey(key))
    {
        dict[key] = new List<string>();
    }
    dict[key].Add(file.FullName);
}
try
{
    var dirs = rootDirectory.EnumerateDirectories();
    return Concat(dict, OverDirectories(dirs));// 采用線程版的方法進行周遊
}
catch (System.Exception e)
{
    Console.WriteLine($"錯誤資訊:{e}");//列印錯誤資訊
}
return dict;           

1.2 資料複用

理想狀态下,我們的資料應該是儲存在資料庫的,但因為資料庫的操作是在下一系列的教程中,是以目前隻能舍棄這個設想。

那麼,利用現有方式,我們可以使用檔案作為緩存的方式,也就是說把資料儲存在檔案裡,在需要的時候從檔案中讀取出來。這時候就需要一組操作檔案的方法。

首先,聲明一個靜态變量:

public static readonly string TempFile = "temp.txt";

然後編寫讀取、存放資料的方法:

public static void WriteLinesToTemp(List lines)

File.AppendAllLines(TempFile, lines);           

public static List Search(string file)

var lines = File.ReadLines(file);
var results = lines.Where(line=>Path.GetFileNameWithoutExtension(line).Contains(file));
return results.ToList();           

這時候在檔案中存放的都是路徑檔案,是以需要重新修改周遊檔案路徑的方法,隻保留路徑:

public static List OverDirectories(DirectoryInfo rootDirectory)

Console.WriteLine($"正在周遊目錄:{rootDirectory.FullName}");
List<string> files = new List<string>();
try
{    
    files.AddRange(rootDirectory.GetFiles().Select(f=>f.FullName).ToList());
    Console.WriteLine($"在目錄:{rootDirectory.FullName} 下 找到 檔案:{files.Count} 個");
}
catch(Exception e)
{
    Console.WriteLine($"加載目錄:{rootDirectory.FullName} 中\t錯誤資訊:{e}");//列印錯誤資訊
}
try
{
    var dirs = rootDirectory.GetDirectories();
    OverDirectories(dirs);
}
catch (System.Exception e)
{
    Console.WriteLine($"在下探目錄{rootDirectory.FullName}時發生錯誤:{e}");
}
return files;           

public static void OverDirectories(IEnumerable directories)

var tasks =new List<Task<List<string>>>( directories.Select(dir => Task.Run(()=>OverDirectories(dir))));
while(tasks.Any())
{
    var completeds = tasks.Where(t=>t.IsCompleted).ToList(); // 提取所有已完成的任務
    foreach(var t in completeds)
    {
        WriteLinesToTemp(t.Result);// 儲存檔案清單
        tasks.Remove(t);//移除已處理的任務
    }
}           

最後修改主方法,設定啟動時周遊路徑的規則:

static void Main(string[] args)

if(!File.Exists(TempFile))// 緩存檔案存在,則認為上次已經周遊成功了
{
    var drivers = GetDrivers();
    OverDirectories(drivers);
}

Console.WriteLine("請輸入要查詢的檔案名:");
var search = Console.ReadLine().Trim();
           

1.3 循環使用并設定退出條件

設定使用者輸入q或Q的時候退出程式,這時候就需要改造Main方法了:

Console.WriteLine("檔案查詢小工具啟動了……");
if(!File.Exists(TempFile))
{
    Console.WriteLine("尚未加載緩存記錄,資料加載中……");
    var drivers = GetDrivers();
    OverDirectories(drivers);
    Console.WriteLine("資料加載完成");
    Thread.Sleep(500);
    Console.Clear();// 清除控制台
}
while(true)
{
    Console.WriteLine("請輸入要查詢的檔案名(輸入q/Q 退出):");
    var search = Console.ReadLine().Trim();// 去除多餘的空白字元
    if(search == "q" || search == "Q")//添加退出條件
    {
        break;
    }
    Console.WriteLine("查詢中……");
    var results = Search(search);
    Console.WriteLine("查詢結果:");
    foreach(var r in results)
    {
        Console.WriteLine(r);
    }
}
Console.WriteLine("程式已退出!");           

在main 方法裡加了很多提示語句,以友善使用。

  1. 總結

    以上是第一次實戰課的所有内容。歡迎各位小夥伴們踴躍讨論。這個小工具并不完善,但是随着我們對.net core的了解和深入就會寫的得心應手了。

原文位址

https://www.cnblogs.com/c7jie/p/12849942.html