如何获取代码所在程序集的路径?

930

有没有一种方法可以获取当前代码所在的程序集路径?我不想要调用程序集的路径,只想要包含代码的程序集。

基本上我的单元测试需要读取一些xml测试文件,这些文件相对于dll文件位于某个位置。无论是从TestDriven.NET、MbUnit GUI还是其他任何地方运行测试dll,我都希望路径能正确解析。

编辑:人们似乎误解了我的意思。

我的测试库位于

C:\projects\myapplication\daotests\bin\Debug\daotests.dll

我想要获取这个路径:

C:\projects\myapplication\daotests\bin\Debug\

到目前为止,三个建议在我从MbUnit Gui运行时都失败了:

  • Environment.CurrentDirectory返回\Program Files\MbUnit

  • System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location 返回C:\Documents and Settings\george\Local Settings\Temp\ ....\DaoTests.dll

  • System.Reflection.Assembly.GetExecutingAssembly().Location 和上一个一样。


128
这是您的解决方案:var dir = AppDomain.CurrentDomain.BaseDirectory; 意思是获取当前应用程序域的基目录,并将其赋值给dir变量。 - Jalal El-Shaer
9
这应该是被接受的解决方案。使用AppDomain.CurrentDomain.BaseDirectory是正确的方法。 - aBetterGamer
2
我来到这里是为了寻找一个解决方案,以便从其包目录中读取JSON文件的NuGet包。似乎当NuGet包被执行时,“AppDomain.CurrentDomain.BaseDirectory”指向运行项目的目录,而不是NuGet包目录。这些方法似乎都无法正确地定位NuGet包目录。 - Lucas
@Lucas 不会,因为这不是这个问题的关键(实际上,在提问时,NuGet 不存在)- 可以自由地提出一个新问题并在其中提到我,但我可以告诉你,大多数情况下这是不可能的。对于大多数项目,NuGet 目录位于 sln 文件旁边的 packages 中。但是,当您编译和分发文件时,没有 sln 文件和 packages 目录。在编译过程中,需要的东西(但不是全部)将被复制到 bin 目录中。最好的方法是使用 postbuild 脚本来复制所需的文件。 - George Mauer
对于那些阅读这些评论并认为 AppDomain.CurrentDomain.BaseDirectory 是正确解决方案的人,请参考 此评论 ,该评论提供了更好的解决方案。 - Neo
31个回答

1175

注意: Assembly.CodeBase 在 .NET Core/.NET 5+ 中已经被弃用: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.codebase?view=net-5.0

我已定义了以下属性,因为我们在单元测试中经常使用它。

public static string AssemblyDirectory
{
    get
    {
        string codeBase = Assembly.GetExecutingAssembly().CodeBase;
        UriBuilder uri = new UriBuilder(codeBase);
        string path = Uri.UnescapeDataString(uri.Path);
        return Path.GetDirectoryName(path);
    }
}
Assembly.Location 属性在使用 NUnit(其中程序集从临时文件夹运行)时有时会给出一些奇怪的结果,因此我更喜欢使用 CodeBase,它以 URI 格式提供路径,然后使用 UriBuild.UnescapeDataString 去除开头的 File://GetDirectoryName 将其更改为常规 Windows 格式。

34
我遇到了一个问题,如果你的目录名称是: c:\My%20Directory 那么使用Uri.UnescapeDataString将返回: c:\My Directory 这意味着File.Exists("c:\My Directory\MyFile.txt")会返回false,因为正确的路径实际上应该是"c:\My%20Directory\MyFile.txt"。我遇到了这个问题,因为我们的SVN路径中有空格,在检出时会对其进行编码。 - row1
7
使用这个方法检查文件是否存在时要小心,因为它在 UNC 路径上会返回 false。使用 @Keith 的答案代替。 - AZ.
6
注意:这不适用于网络位置(例如 \REMOTE_PC\Folder)。 - Muxa
6
如果目录名称中包含井号'#',那么这个方法将无法使用。在Windows系统中,目录和文件名是允许包含井号的。 - Huemac
10
CodeBase已经过时,Assembly.Location是更好的替代选择。 - Stefan
显示剩余14条评论

369

就是这么简单:

var dir = AppDomain.CurrentDomain.BaseDirectory;

13
这应该是被接受的解决方案。使用AppDomain.CurrentDomain.BaseDirectory是正确的方法。 - aBetterGamer
6
感谢你让我注意到这一点——不确定在我提出问题时是否可以使用,但现在可以了。 - George Mauer
146
不,这是错误的。它返回的是原始入口点的路径,而不是当前正在执行的代码的路径。如果您手动从不同路径加载程序集,或者它已从全局程序集缓存加载,则它将返回错误的结果。这个回答是正确的:https://dev59.com/EHVD5IYBdhLWcg3wNIzc#283917 更快的方法是 Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) - nathanchere
12
实际上,这种方法在 Web 应用程序中不起作用,但是据我所发现,以下的增强应该适用于任何类型的应用程序:AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory - Ilya Chernomordik
4
如果您只想获取测试程序集的原始二进制路径(例如,访问子文件夹中的辅助数据文件),则这非常适用于单元测试。测试程序集是代码的入口点。 - MarioDS
显示剩余5条评论

367

这有所帮助吗?

//get the full location of the assembly with DaoTests in it
string fullPath = System.Reflection.Assembly.GetAssembly(typeof(DaoTests)).Location;

//get the folder that's in
string theDirectory = Path.GetDirectoryName( fullPath );

3
将XML文件设置为内容,与DLL一起复制,或从DLL中读取。 - Keith
31
或者只需使用 typeof(DaoTests).Assembly。该语句用于获取类型 DaoTests 的程序集信息,并返回程序集对象。 - SLaks
2
我个人会选择这样的方法: public static string GetAssemblyDirectory<T>(){return System.IO.Path.GetDirectoryName(typeof(T).Assembly.Location);} - Johny Skovdal
4
@SLaks @JohnySkovdal @Keith:嘿,使用 Assembly.GetExecutingAssembly()。它从方法描述中可以看到,_"获取包含当前正在执行的代码的程序集"_。我在我的AddIn "EntitiesToDTOs" 中使用它。请查看 AssemblyHelper.cs 获取真实的例子。 - kzfabi
4
@John Silby的帖子存在问题,因为它似乎不适用于UNC路径...例如\Server\Folder\File.ext。这个解决了问题。+1 - Blueberry
显示剩余8条评论

79

和John的答案一样,但是这是一个稍微简洁一点的扩展方法。

public static string GetDirectoryPath(this Assembly assembly)
{
    string filePath = new Uri(assembly.CodeBase).LocalPath;
    return Path.GetDirectoryName(filePath);            
}

现在你可以这样做:

var localDir = Assembly.GetExecutingAssembly().GetDirectoryPath();

或者如果你更喜欢:

var localDir = typeof(DaoTests).Assembly.GetDirectoryPath();

6
你的意思是使用 assembly 而不是 Assembly.GetExecutingAssembly() 吗? - Dude Pascalou
3
正如 Dude 指出的那样,你传递了一个参数但未使用它。 - Chris Moschini
4
这个回答对于当前问题来说是完全错误的。修改过的这个回答可以给出给定程序集的路径。然而在这里,我们特别寻找正在执行的程序集,因此传入一个程序集是没有意义的。扩展方法并不适合这个任务。 - Edward Brey
如何获取位置而不带有尾随的 bin/Debug/netcoreapp 等内容? - Aaron Franke
这解决了当像 Assembly.GetExecutingAssembly().GetDirectoryPath() 这样调用时的问题。而且,使用 new Uri 比使用 John Sibly 的答案中的 UriBuilderUnescapeDataString 更加简洁。 - Fanblade

52

当我使用CodeBase和UNC网络共享时,唯一对我有效的解决方案是:

System.IO.Path.GetDirectoryName(new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath);

它也适用于普通URI。


5
这应该是被接受的答案。默认的代码库不能正确处理UNC共享,这真的很恼人。 - Daniel Gilbert
当文件夹包含空格和其它字符时,这种方法就会崩溃... - MarioDS
1
我已经经常使用它,并发现只有一个场景会失败:如果这行代码本身是NuGet包的一部分,然后被应用程序使用!我们也可以通过将GetExecutingAssembly()替换为GetCallingAssembly()来支持该场景。 - Timo
@Timo:你验证过这个更改是否有副作用了吗?如果有,请编辑答案并包含修复。 - Ignacio Soler Garcia
很遗憾,我必须报告它只能在一层深度下工作,即如果NuGet包被另一个NuGet包调用,则会失败!现在我正在使用这个(来自Chernomordik在此页面上的评论):AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory。第一部分适用于Web应用程序,第二部分适用于其他应用程序。 - Timo

40

这应该可以工作,除非程序集被阴影复制

string path = System.Reflection.Assembly.GetExecutingAssembly().Location

值得指出的是,在Web应用程序的情况下,dll文件会被影子复制,因此上面大部分建议将返回“Temporary ASP.NET Files”的子文件夹。这个答案至少指出了影子复制元素。 - b_levitt

28

我相信这对任何类型的应用程序都适用:

AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory

3
我的实验表明这是最为可靠的答案,不仅覆盖了Web和控制台应用程序,还包括单元测试和NuGet包中的调用(嵌套到任意递归级别)。 - Timo
1
感谢您提供这个优雅的解决方案! - ecif
同样适用于ASP.net应用程序(程序集文件被影子复制到临时ASP.NET文件夹)。 - b_levitt

19

从.net framework 4.6 / .net core 1.0开始,现在有一个AppContext.BaseDirectory,它应该会给出与AppDomain.CurrentDomain.BaseDirectory相同的结果,除了AppDomains不是 .net core 1.x / .net standard 1.x API 的一部分。

AppContext.BaseDirectory

编辑:文档现在甚至说明:

在 .NET 5.0 及更高版本中,对于捆绑的程序集,返回的值是托管可执行文件所在目录。

事实上,Assembly.Location doc 文档说:

在 .NET 5.0 及更高版本中,对于捆绑的程序集,返回的值为空字符串。


根据AOT警告,这是正确的答案:“警告IL3000:对于嵌入在单文件应用程序中的程序集,'System.Reflection.Assembly.Location'始终返回空字符串。如果需要应用程序目录的路径,请考虑调用'System.AppContext.BaseDirectory'。” - undefined

15
AppDomain.CurrentDomain.BaseDirectory

适用于MbUnit GUI。


1
在 asp.net web 应用程序中,相对于根目录编写文件时,这非常有效。 - Philip Pittle
我发现这个通常效果最好。如果不确定,选择它。 - Erik Bergstedt
这是唯一正确的答案,当程序集被合并时,所有其他答案都已过时且无法工作,因为位置将为空。 - Walter Verhoeven

15

这个怎么样:

System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

不支持 .NET 5 捆绑的程序集,参见 https://dev59.com/EHVD5IYBdhLWcg3wNIzc#62626131。 - cube45

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接