筆者的九篇反射系統文章已經完結,但是筆者會持續更新反射在日常擼碼中的應用。
本篇內容主要是 .NET Core 獲取運行環境信息、利用反射更加方便地處理數據。
本篇內容有:RuntimeInformation、Environment、反射、特性等。
本篇代碼下載地址 https://gitee.com/whuanle/reflection_and_properties/blob/master/反射特性應用場景1.cs
獲取示例:
筆者的九篇反射系列文章閱讀地址如下:
C# 反射與特性(一):反射基礎
C# 反射與特性(六):實現 ASP.NET Core 依賴注入 Web
RuntimeInformation、Environment
RuntimeInformation 類提供有關 .NET 運行時安裝的信息。主要獲取平台以及 版本,API較少。
文檔地址 https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.interopservices.runtimeinformation?view=netcore-3.1
Environment 提供有關當前環境和平台的信息以及操作它們的方法。API比較多。
文檔地址 https://docs.microsoft.com/zh-cn/dotnet/api/system.environment?view=netcore-3.1
以上兩個類已經提供了文檔地址,這里不再贅述。
需要注意的是,Windows、Linux 之間有差異,因此有些 API 是無法跨平台的。另外 .NET Core 相對 .NET Framework ,對獲取系統資源信息等的 API 十分少。
.NET Core 是無有 API 獲取系統 CPU 情況和 內存使用情況,倒是可以獲取當前進程的 CPU 和 內存使用情況。
可以查看 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 特性,專業用來顯示信息項的。這樣設置,可以為屬性靈活設置別名,便於顯示信息以及說明。
筆者會繼續帶來更多反射的使用實例,融入到日常需求中。