C#如何无需为每个类型新增属性即可获取其对应的.cs文件路径
方案1:基于StackTrace + PDB实现(无需修改现有代码)
异常能获取源文件路径的核心是程序集对应的PDB文件中存储了IL指令和源文件位置的映射关系,直接通过StackTrace读取该信息即可,不需要为每个类型新增属性:
using System.Diagnostics; using System.Reflection; public static class SourceFileLocator { /// <summary> /// 获取指定类型对应的.cs源文件路径 /// </summary> /// <param name="type">目标类型</param> /// <returns>源文件绝对路径,无对应PDB时返回null</returns> public static string GetCSharpFilePath(Type type) { // 优先取类型的构造函数,避免接口/抽象类无实现方法的问题 MethodBase method = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) .FirstOrDefault(); // 无构造函数时取第一个非抽象方法 method ??= type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) .FirstOrDefault(m => !m.IsAbstract); if (method == null) return null; // 第二个参数设为true表示捕获源文件信息 StackTrace stackTrace = new StackTrace(method, true); StackFrame frame = stackTrace.GetFrame(0); return frame?.GetFileName(); } }
适用场景
- 环境中存在对应程序集的PDB调试文件(默认Debug、Release发布模式都会生成,仅当手动勾选删除调试信息、使用裁剪/AOT编译时会失效)
- 支持获取任意非抽象类型的源文件路径
如果需要批量获取所有实现了指定接口的类型路径,可以扩展如下方法:
using System.Reflection; using System.Linq; public static IEnumerable<(Type ImplementType, string FilePath)> GetAllInterfaceImplPaths<TInterface>() { // 可按需替换为目标程序集 Assembly targetAssembly = Assembly.GetExecutingAssembly(); var implTypes = targetAssembly.GetTypes() .Where(t => typeof(TInterface).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); foreach (var type in implTypes) { string path = GetCSharpFilePath(type); if (!string.IsNullOrEmpty(path)) { yield return (type, path); } } }
方案2:基于源生成器实现(无PDB也可使用,性能更高)
如果需要在无PDB的生产环境使用,且使用C# 9及以上版本,可以通过增量源生成器自动为所有目标类型插入路径属性,不需要手动修改每个类的代码:
- 实现一个增量源生成器,扫描项目中所有实现目标接口的类型
- 自动为每个匹配的生成分部类代码,插入编译时生成的路径属性:
// 源生成器自动生成的分部类代码示例 public partial class YourTargetType { public string SourceFilePath { get; } = GetCallerFilePath(); private static string GetCallerFilePath([System.Runtime.CompilerServices.CallerFilePath] string path = default) => path; }
适用场景
- 目标类型均为当前项目可编译的代码,不支持第三方程序集类型
- 编译时完成路径注入,运行时无性能损耗,不需要依赖PDB文件
说明
你之前尝试的基类中加[CallerFilePath]无法获取子类路径的问题,是因为该特性是编译时替换属性,基类的代码编译时就被替换为基类的源文件路径,子类继承时不会重新计算,因此无法实现需求。
内容的提问来源于stack exchange,提问作者Xavier Peña
相关产品推荐
相关产品推荐

