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 十分少。
.NET Core 是无有 API 获取系统 CPU 情况和 内存使用情况,倒是可以获取当前进程的 CPU 和 内存使用情况。
可以查看 stackoverflow 了解。
获取信息
下面获取的属于进程使用的内存已经使用 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 特性,专业用来显示信息项的。这样设置,可以为属性灵活设置别名,便于显示信息以及说明。
笔者会继续带来更多反射的使用实例,融入到日常需求中。