1. 程式人生 > >.NET Core 獲取程式執行環境資訊與反射的應用

.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較少。

文件地址 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 特性,專業用來顯示資訊項的。這樣設定,可以為屬性靈活設定別名,便於顯示資訊以及說明。

筆者會繼續帶來更多反射的使用例項,融入到日常需求