C#怎么获取程序的编译时间_C#如何读取程序集的属性信息【技巧】

3次阅读

最可靠方式是通过 AssemblyMetadataAttribute 在编译时注入并读取 BuildTime;其次可解析 AssemblyVersion 中自动生成的构建号(需项目配置时间戳版本);文件时间戳(如 LastWriteTime)仅为妥协方案,不可靠。

C#怎么获取程序的编译时间_C#如何读取程序集的属性信息【技巧】

怎么从 Assembly 里读出编译时间?

没有直接的“编译时间”元数据——.NET 不在程序集里存这个值。所谓“获取编译时间”,本质是读取 AssemblyVersion 或文件时间戳,再靠约定反推。最常用、也最靠谱的方式是解析 AssemblyVersion 中的构建号(build number),前提是你的项目用了自动生成版本号(比如 1.0.* 或 SDK 风格项目的 $(DateTime) 替换)。

如果没配自动版本,Assembly.GetExecutingAssembly().GetName().VersionBuildRevision 就是 0,毫无意义。这时候只能退而求其次,读程序集文件的 LastWriteTime(但注意:它反映的是最后写入磁盘的时间,不是编译时间;发布后重签名、复制、解压都会改它)。

  • SDK 风格项目推荐在 .csproj 中启用时间戳版本:<VersionPrefix>1.0.0</VersionPrefix><VersionSuffix>$(DateTime:yyyyMMddHHmm)</VersionSuffix>
  • 老式项目可用 AssemblyVersion("1.0.*"),此时 Build 是自动生成的天数(距 2000-1-1),Revision 是秒数 / 2,可换算
  • 别信 File.GetCreationTime——它在很多部署场景下不可靠(如 ZIP 解压、CI/CD 拷贝)

GetCustomAttribute<AssemblyMetadataAttribute> 能不能存编译时间?

可以,而且是真正可控的方式:你在编译时手动注入一条元数据,运行时再读出来。但它需要你主动配置 MSBuild,在生成阶段把时间写进去。

原理很简单:MSBuild 执行时用 $(DateTime)$(UnixTimestamp) 生成一个字符串,通过 <AssemblyMetadata> 写进程序集。运行时调用 Assembly.GetCustomAttribute<AssemblyMetadataAttribute>("BuildTime") 就能拿到。

  • .csproj 里加:<AssemblyMetadata include="BuildTime" Value="$(DateTime:yyyy-MM-dd HH:mm:ss)" />
  • 注意 DateTime 格式不支持所有分隔符,MM(月)和 mm(分)大小写敏感,错写成 mm 会报 MSBuild 错误
  • 读取时必须用完全匹配的 key 名,"buildtime""BuildTime" 是不同的键
  • 该方式不影响性能,也不依赖文件系统,适合 CI/CD 环境固化构建信息

为什么 Assembly.GetExecutingAssembly().location 有时为空?

因为当前程序集被加载为“反射上下文”或“内存流”,比如单元测试里用 Assembly.Load(byte[])、Blazor WebAssembly、某些插件热加载场景,Location 返回空字符串。这时候 CodeBase 也可能不可靠,甚至抛异常。

想绕过这个问题,得换路径策略:优先用 Assembly.GetExecutingAssembly().ManifestModule.Name 拿模块名,再结合 AppContext.BaseDirectory 拼路径;或者更稳妥地,用 typeof(Program).Assembly.Location(确保 Program 类在主程序集中)。

  • 不要对 Location 做非空断言,先判空再 fallback
  • Assembly.GetCallingAssembly() 在 JIT 或内联后可能指向意外程序集,慎用
  • unity 或 .NET Native AOT 下,反射行为受限,AssemblyMetadata 可能被裁剪,需保留 [assembly: Preserve](若用 Mono)或检查 AOT 链接配置

AssemblyDescriptionAttributeAssemblyProductAttribute 有啥陷阱?

这些属性是纯字符串,编译器不校验内容,也不强制填写。常见问题是空值、硬编码占位符(如 "TODO: Enter description")、或混入换行符导致 UI 显示异常。

它们适合放人工维护的信息(如产品名、版权),不适合放机器生成的数据(比如时间戳)。如果你看到某处代码试图从 AssemblyDescriptionAttribute 里 parse 时间,基本可以判定是历史债务——没人维护、格式不统一、容易崩。

  • 读取前务必判空:attr?.Description ?? "N/A"
  • 不要用正则从 Description 里抽时间——不同团队填法五花八门,今天是 "Built on 2024-05-20",明天可能是 "v1.2.3 (20240520)"
  • 这类属性会被写进 windows PE 文件的资源段,影响最终 EXE 大小(虽然极小),但过度砌描述字段无实际收益

真正可靠的编译时间,只存在于你主动写进去的地方。要么塞进 AssemblyVersion(靠约定),要么用 AssemblyMetadata(靠配置),其余都是妥协方案。最容易被忽略的一点:时间格式必须在写入和读取两端严格一致,哪怕多一个空格,DateTime.TryParseExact 就会静默失败。

text=ZqhQzanResources