.NET Core 獲取程式執行環境資訊與反射的應用
目錄
- RuntimeInformation、Environment
- 獲取資訊
- 反射獲取資訊
- 獲取某個屬性的值以及別名
- 反射獲取資訊
- 使用
- 總結
筆者的九篇反射系統文章已經完結,但是筆者會持續更新反射在日常擼碼中的應用。
本篇內容主要是 .NET Core 獲取執行環境資訊、利用反射更加方便地處理資料。
本篇內容有:RuntimeInformation、Environment、反射、特性等。
本篇程式碼下載地址 https://gitee.com/whuanle/reflection_and_properties/blob/master/反射特性應用場景1.cs
獲取示例:
筆者的九篇反射系列文章閱讀地址如下:
C# 反射與特性(一):反射基礎
C# 反射與特性(二):探究反射
C# 反射與特性(三):反射型別的成員
C# 反射與特性(四):例項化型別
C# 反射與特性(五):型別成員操作
C# 反射與特性(六):實現 ASP.NET Core 依賴注入 Web
C# 反射與特性(七):自定義特性以及應用
C# 反射與特性(八):反射操作例項大全
C# 反射與特性(九):解析反射例項大全
RuntimeInformation、Environment
RuntimeInformation 類提供有關 .NET 執行時安裝的資訊。主要獲取平臺以及 版本,API較少。
Environment 提供有關當前環境和平臺的資訊以及操作它們的方法。API比較多。
文件地址 https://docs.microsoft.com/zh-cn/dotnet/api/system.environment?view=netcore-3.1
以上兩個類已經提供了文件地址,這裡不再贅述。
需要注意的是,Windows、Linux 之間有差異,因此有些 API 是無法跨平臺的。另外 .NET Core 相對 .NET Framework ,對獲取系統資源資訊等的 API 十分少。
可以檢視 stackoverflow 瞭解。
https://stackoverflow.com/questions/54215334/how-to-measure-cpu-usage-and-memory-for-a-process-in-net-core-linux
獲取資訊
下面獲取的屬於程序使用的記憶體已經使用 CPU 時間。
CPU 時間不像直接獲取到的 使用百分比,可以很直觀地看到。
CPU 時間地公式如下。
CPU時間 = 執行程式所需的時鐘週期數 * 時鐘週期時間
CPU 有多核多執行緒,因此不能按照執行多長時間去計算。同時程序存在休眠、上下文切換等情況。
程式運行了幾小時,有可能CPU時間只有幾十分鐘。
對 CPU 效能計算方法有興趣,請參考 https://www.cnblogs.com/whuanle/p/12260224.html
對 Linux CPU 使用率計算有興趣,請檢視 https://www.cnblogs.com/aresxin/p/9152127.html
我們在 C# 中使用地程式碼如下
[Display(Name = "執行資訊")]
public class ApplicationRunInfo
{
private double _UsedMem;
private double _UsedCPUTime;
public ApplicationRunInfo()
{
var proc = Process.GetCurrentProcess();
var mem = proc.WorkingSet64;
var cpu = proc.TotalProcessorTime;
_UsedMem = mem / 1024.0;
_UsedCPUTime = cpu.TotalMilliseconds;
}
[Display(Name = "程序已使用實體記憶體(kb)")]
public double UsedMem { get { return _UsedMem; } }
[Display(Name = "程序已佔耗CPU時間(ms)")]
public double UsedCPUTime { get { return _UsedCPUTime; } }
}
這裡只有兩個屬性。
我們使用 Display 特性來標記此屬性地的含義,方便反射時獲取資訊。
另外還有兩個獲取不同型別資訊的類如下
[Display(Name = "系統執行平臺")]
public class SystemPlatformInfo
{
[Display(Name = "執行框架")]
public string FrameworkDescription { get { return RuntimeInformation.FrameworkDescription; } }
[Display(Name = "作業系統")]
public string OSDescription { get { return RuntimeInformation.OSDescription; } }
[Display(Name = "作業系統版本")]
public string OSVersion { get { return Environment.OSVersion.ToString(); } }
[Display(Name = "平臺架構")]
public string OSArchitecture { get { return RuntimeInformation.OSArchitecture.ToString(); } }
}
[Display(Name = "執行環境")]
public class SystemRunEvnInfo
{
[Display(Name = "機器名稱")]
public string MachineName { get { return Environment.MachineName; } }
[Display(Name = "使用者網路域名")]
public string UserDomainName { get { return Environment.UserDomainName; } }
[Display(Name = "分割槽磁碟")]
public string GetLogicalDrives { get { return string.Join(", ", Environment.GetLogicalDrives()); } }
[Display(Name = "系統目錄")]
public string SystemDirectory { get { return Environment.SystemDirectory; } }
[Display(Name = "系統已執行時間(毫秒)")]
public int TickCount { get { return Environment.TickCount; } }
[Display(Name = "是否在互動模式中執行")]
public bool UserInteractive { get { return Environment.UserInteractive; } }
[Display(Name = "當前關聯使用者名稱")]
public string UserName { get { return Environment.UserName; } }
[Display(Name = "Web程式核心框架版本")]
public string Version { get { return Environment.Version.ToString(); } }
//對Linux無效
[Display(Name = "磁碟分割槽")]
public string SystemDrive { get { return Environment.ExpandEnvironmentVariables("%SystemDrive%"); } }
//對Linux無效
[Display(Name = "系統目錄")]
public string SystemRoot { get { return Environment.ExpandEnvironmentVariables("%SystemRoot%"); } }
}
可能你會覺得,為什麼不寫成方法,為啥要寫得這麼奇怪。不急,慢慢看下去~
反射獲取資訊
我們來定義一個靜態型別,作為獲取各種資訊的入口。
public static class EnvironmentInfo
{
}
}
獲取屬性值
反射獲取屬性值的方法,用於獲取上述幾個類的屬性值。
/// <summary>
/// 獲取屬性的值
/// </summary>
/// <param name="info"></param>
/// <param name="obj">例項</param>
/// <returns></returns>
private static object GetPropertyInfoValue(PropertyInfo info, object obj)
{
return info.GetValue(obj);
}
反射獲取特性值
我們使用了特性 [Display(Name = "當前關聯使用者名稱")]
來儲存別名。
我們要通過反射獲取 Dispaly
特性的 Name 屬性值。
/// <summary>
/// 獲取 [Display] 特性的屬性 Name 的值
/// </summary>
/// <param name="attrs"></param>
/// <returns></returns>
private static string GetDisplayNameValue(IList<CustomAttributeData> attrs)
{
var argument = attrs.FirstOrDefault(x => x.AttributeType.Name == nameof(DisplayAttribute)).NamedArguments;
return argument.FirstOrDefault(x => x.MemberName == nameof(DisplayAttribute.Name)).TypedValue.Value.ToString();
}
獲取某個屬性的值以及別名
我們使用了這樣的方式去設定獲取一項資訊
[Display(Name = "作業系統")]
public string OSDescription { get { return RuntimeInformation.OSDescription; } }
因此我們要獲取到一個型別所有的屬性值和屬性的特性值。
/// <summary>
/// 獲取某個型別的值以及名稱
/// </summary>
/// <typeparam name="TInfo"></typeparam>
/// <param name="info"></param>
/// <returns></returns>
private static (string, List<KeyValuePair<string, object>>) GetValues<TInfo>(TInfo info)
{
List<KeyValuePair<string, object>> list = new List<KeyValuePair<string, object>>();
Type type = info.GetType();
PropertyInfo[] pros = type.GetProperties();
foreach (var item in pros)
{
var name = GetDisplayNameValue(item.GetCustomAttributesData());
var value = GetPropertyInfoValue(item, info);
list.Add(new KeyValuePair<string, object>(name, value));
}
return
(GetDisplayNameValue(info.GetType().GetCustomAttributesData()),
list);
}
反射獲取資訊
上面的工具方法定義後,我們來設定不同的方法獲取不同的資訊。
/// <summary>
/// 獲取程式執行資源資訊
/// </summary>
/// <returns></returns>
public static (string, List<KeyValuePair<string, object>>) GetApplicationRunInfo()
{
ApplicationRunInfo info = new ApplicationRunInfo();
return GetValues(info);
}
/// <summary>
/// 獲取系統執行平臺資訊
/// </summary>
/// <returns></returns>
public static (string, List<KeyValuePair<string, object>>) GetSystemPlatformInfo()
{
SystemPlatformInfo info = new SystemPlatformInfo();
return GetValues(info);
}
/// <summary>
/// 獲取系統執行環境資訊
/// </summary>
/// <returns></returns>
public static (string, List<KeyValuePair<string, object>>) GetSystemRunEvnInfo()
{
SystemRunEvnInfo info = new SystemRunEvnInfo();
return GetValues(info);
}
還有一個方法獲取環境變數的,不需要利用上面的型別-屬性來操作,可以直接封裝到方法中。
/// <summary>
/// 獲取系統全部環境變數
/// </summary>
/// <returns></returns>
public static (string, List<KeyValuePair<string, object>>) GetEnvironmentVariables()
{
List<KeyValuePair<string, object>> list = new List<KeyValuePair<string, object>>();
IDictionary environmentVariables = Environment.GetEnvironmentVariables();
foreach (DictionaryEntry de in environmentVariables)
{
list.Add(new KeyValuePair<string, object>(de.Key.ToString(), de.Value));
}
return ("系統環境變數", list);
}
使用
我們在 Program 中,這些寫就可以輸出所有資訊了
static void Main(string[] args)
{
var a = EnvironmentInfo.GetApplicationRunInfo();
var b = EnvironmentInfo.GetSystemPlatformInfo();
var c = EnvironmentInfo.GetSystemRunEvnInfo();
var d = EnvironmentInfo.GetEnvironmentVariables();
ConsoleInfo(a.Item1, a.Item2);
ConsoleInfo(b.Item1, b.Item2);
ConsoleInfo(c.Item1, c.Item2);
ConsoleInfo(d.Item1, d.Item2);
Console.ReadKey();
}
public static void ConsoleInfo(string title, List<KeyValuePair<string, object>> list)
{
Console.WriteLine("\n***********" + title + "***********");
foreach (var item in list)
{
Console.WriteLine(item.Key + ":" + item.Value);
}
}
在 Linux 中顯示
總結
我以上使用了 類-屬性 來作為獲取功能,這樣可以不必寫很多方法去呼叫獲取環境資訊,屬性就是資料。既方便序列化,又方便反射。
同時,如果先拓展資訊項,直接新增上去就行,反射直接全部拿到手。
另外有個 Display 特性,專業用來顯示資訊項的。這樣設定,可以為屬性靈活設定別名,便於顯示資訊以及說明。
筆者會繼續帶來更多反射的使用例項,融入到日常需求