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

怎么从 Assembly 里读出编译时间?
没有直接的“编译时间”元数据——.NET 不在程序集里存这个值。所谓“获取编译时间”,本质是读取 AssemblyVersion 或文件时间戳,再靠约定反推。最常用、也最靠谱的方式是解析 AssemblyVersion 中的构建号(build number),前提是你的项目用了自动生成版本号(比如 1.0.* 或 SDK 风格项目的 $(DateTime) 替换)。
如果没配自动版本,Assembly.GetExecutingAssembly().GetName().Version 的 Build 和 Revision 就是 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 链接配置
读 AssemblyDescriptionAttribute 或 AssemblyProductAttribute 有啥陷阱?
这些属性是纯字符串,编译器不校验内容,也不强制填写。常见问题是空值、硬编码占位符(如 "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 就会静默失败。










