<p>From the <em>Immediate Window</em> in Visual Studio: </p>

<pre><code>&gt; Path.Combine(@"C:\x", "y")
"C:\\x\\y"
&gt; Path.Combine(@"C:\x", @"\y")
"\\y"
</code></pre>

<p>It seems that they should both be the same.  </p>

<p>The old FileSystemObject.BuildPath() didn't work this way...</p>

[{'answer_id': 53110, 'author': 'elarson', 'author_id': 5434, 'author_profile': 'https://Stackoverflow.com/users/5434', 'pm_score': 2, 'selected': False, 'text': "<p>Not knowing the actual details, my guess is that it makes an attempt to join like you might join relative URIs. For example:</p>\n\n<pre><code>urljoin('/some/abs/path', '../other') = '/some/abs/other'\n</code></pre>\n\n<p>This means that when you join a path with a preceding slash, you are actually joining one base to another, in which case the second gets precedence. </p>\n"}, {'answer_id': 53113, 'author': 'nickd', 'author_id': 2373, 'author_profile': 'https://Stackoverflow.com/users/2373', 'pm_score': 3, 'selected': False, 'text': '<p>From <a href="https://msdn.microsoft.com/en-us/library/fyy7a5kt.aspx" rel="nofollow noreferrer">MSDN</a>:</p>\n\n<blockquote>\n  <p>If one of the specified paths is a zero-length string, this method returns the other path. If path2 contains an absolute path, this method returns path2. </p>\n</blockquote>\n\n<p>In your example, path2 is absolute.</p>\n'}, {'answer_id': 53118, 'author': 'Ryan Lundy', 'author_id': 5486, 'author_profile': 'https://Stackoverflow.com/users/5486', 'pm_score': 9, 'selected': True, 'text': '<p>This is kind of a philosophical question (which perhaps only Microsoft can truly answer), since it\'s doing exactly what the documentation says.</p>\n\n<p><a href="http://msdn.microsoft.com/en-us/library/system.io.path.combine.aspx" rel="noreferrer">System.IO.Path.Combine</a></p>\n\n<p>"If path2 contains an absolute path, this method returns path2."</p>\n\n<p><a href="http://referencesource.microsoft.com/#mscorlib/system/io/path.cs,2d7263f86a526264" rel="noreferrer">Here\'s the actual Combine method</a> from the .NET source.  You can see that it calls <a href="http://referencesource.microsoft.com/mscorlib/system/io/path.cs.html#16ed6da326ce4745" rel="noreferrer">CombineNoChecks</a>, which then calls <a href="http://referencesource.microsoft.com/mscorlib/system/io/path.cs.html#807960f08fca497d" rel="noreferrer">IsPathRooted</a> on path2 and returns that path if so:</p>\n\n<pre><code>public static String Combine(String path1, String path2) {\n    if (path1==null || path2==null)\n        throw new ArgumentNullException((path1==null) ? "path1" : "path2");\n    Contract.EndContractBlock();\n    CheckInvalidPathChars(path1);\n    CheckInvalidPathChars(path2);\n\n    return CombineNoChecks(path1, path2);\n}\n\ninternal static string CombineNoChecks(string path1, string path2)\n{\n    if (path2.Length == 0)\n        return path1;\n\n    if (path1.Length == 0)\n        return path2;\n\n    if (IsPathRooted(path2))\n        return path2;\n\n    char ch = path1[path1.Length - 1];\n    if (ch != DirectorySeparatorChar &amp;&amp; ch != AltDirectorySeparatorChar &amp;&amp;\n            ch != VolumeSeparatorChar) \n        return path1 + DirectorySeparatorCharAsString + path2;\n    return path1 + path2;\n}\n</code></pre>\n\n<p>I don\'t know what the rationale is.  I guess the solution is to strip off (or Trim) DirectorySeparatorChar from the beginning of the second path; maybe write your own Combine method that does that and then calls Path.Combine().</p>\n'}, {'answer_id': 53122, 'author': 'Gulzar Nazim', 'author_id': 4337, 'author_profile': 'https://Stackoverflow.com/users/4337', 'pm_score': 5, 'selected': False, 'text': '<p>This is the disassembled code from <a href="http://en.wikipedia.org/wiki/.NET_Reflector" rel="nofollow noreferrer">.NET Reflector</a> for Path.Combine method. Check IsPathRooted function. If the second path is rooted (starts with a DirectorySeparatorChar), return second path as it is.</p>\n\n<pre><code>public static string Combine(string path1, string path2)\n{\n    if ((path1 == null) || (path2 == null))\n    {\n        throw new ArgumentNullException((path1 == null) ? "path1" : "path2");\n    }\n    CheckInvalidPathChars(path1);\n    CheckInvalidPathChars(path2);\n    if (path2.Length == 0)\n    {\n        return path1;\n    }\n    if (path1.Length == 0)\n    {\n        return path2;\n    }\n    if (IsPathRooted(path2))\n    {\n        return path2;\n    }\n    char ch = path1[path1.Length - 1];\n    if (((ch != DirectorySeparatorChar) &amp;&amp;\n         (ch != AltDirectorySeparatorChar)) &amp;&amp;\n         (ch != VolumeSeparatorChar))\n    {\n        return (path1 + DirectorySeparatorChar + path2);\n    }\n    return (path1 + path2);\n}\n\n\npublic static bool IsPathRooted(string path)\n{\n    if (path != null)\n    {\n        CheckInvalidPathChars(path);\n        int length = path.Length;\n        if (\n              (\n                  (length &gt;= 1) &amp;&amp;\n                  (\n                      (path[0] == DirectorySeparatorChar) ||\n                      (path[0] == AltDirectorySeparatorChar)\n                  )\n              )\n\n              ||\n\n              ((length &gt;= 2) &amp;&amp;\n              (path[1] == VolumeSeparatorChar))\n           )\n        {\n            return true;\n        }\n    }\n    return false;\n}\n</code></pre>\n'}, {'answer_id': 53551, 'author': 'Wedge', 'author_id': 332, 'author_profile': 'https://Stackoverflow.com/users/332', 'pm_score': 5, 'selected': False, 'text': '<p>In my opinion this is a bug. The problem is that there are two different types of "absolute" paths. The path "d:\\mydir\\myfile.txt" is absolute, the path "\\mydir\\myfile.txt" is also considered to be "absolute" even though it is missing the drive letter. The correct behavior, in my opinion, would be to prepend the drive letter from the first path when the second path starts with the directory separator (and is not a UNC path). I would recommend writing your own helper wrapper function which has the behavior you desire if you need it.</p>\n'}, {'answer_id': 23255902, 'author': 'Estevez', 'author_id': 617640, 'author_profile': 'https://Stackoverflow.com/users/617640', 'pm_score': 0, 'selected': False, 'text': '<p>This \\ means "the root directory of the current drive". In your example it means the "test" folder in the current drive\'s root directory. So, this can be equal to "c:\\test".</p>\n'}, {'answer_id': 24823642, 'author': 'Ferri', 'author_id': 3780016, 'author_profile': 'https://Stackoverflow.com/users/3780016', 'pm_score': 2, 'selected': False, 'text': '<p>If you want to combine both paths without losing any path you can use this:</p>\n\n<pre><code>?Path.Combine(@"C:\\test", @"\\test".Substring(0, 1) == @"\\" ? @"\\test".Substring(1, @"\\test".Length - 1) : @"\\test");\n</code></pre>\n\n<p>Or with variables:</p>\n\n<pre><code>string Path1 = @"C:\\Test";\nstring Path2 = @"\\test";\nstring FullPath = Path.Combine(Path1, Path2.IsRooted() ? Path2.Substring(1, Path2.Length - 1) : Path2);\n</code></pre>\n\n<p>Both cases return "C:\\test\\test".</p>\n\n<p>First, I evaluate if Path2 starts with / and if it is true, return Path2 without the first character. Otherwise, return the full Path2.</p>\n'}, {'answer_id': 27271258, 'author': 'The King', 'author_id': 733566, 'author_profile': 'https://Stackoverflow.com/users/733566', 'pm_score': 3, 'selected': False, 'text': "<p>This code should do the trick:            </p>\n\n<pre><code>        string strFinalPath = string.Empty;\n        string normalizedFirstPath = Path1.TrimEnd(new char[] { '\\\\' });\n        string normalizedSecondPath = Path2.TrimStart(new char[] { '\\\\' });\n        strFinalPath =  Path.Combine(normalizedFirstPath, normalizedSecondPath);\n        return strFinalPath;\n</code></pre>\n"}, {'answer_id': 31131504, 'author': 'anhoppe', 'author_id': 1178267, 'author_profile': 'https://Stackoverflow.com/users/1178267', 'pm_score': 5, 'selected': False, 'text': '<p>I wanted to solve this problem:</p>\n\n<pre><code>string sample1 = "configuration/config.xml";\nstring sample2 = "/configuration/config.xml";\nstring sample3 = "\\\\configuration/config.xml";\n\nstring dir1 = "c:\\\\temp";\nstring dir2 = "c:\\\\temp\\\\";\nstring dir3 = "c:\\\\temp/";\n\nstring path1 = PathCombine(dir1, sample1);\nstring path2 = PathCombine(dir1, sample2);\nstring path3 = PathCombine(dir1, sample3);\n\nstring path4 = PathCombine(dir2, sample1);\nstring path5 = PathCombine(dir2, sample2);\nstring path6 = PathCombine(dir2, sample3);\n\nstring path7 = PathCombine(dir3, sample1);\nstring path8 = PathCombine(dir3, sample2);\nstring path9 = PathCombine(dir3, sample3);\n</code></pre>\n\n<p>Of course, all paths 1-9 should contain an equivalent string in the end. Here is the PathCombine method I came up with:</p>\n\n<pre><code>private string PathCombine(string path1, string path2)\n{\n    if (Path.IsPathRooted(path2))\n    {\n        path2 = path2.TrimStart(Path.DirectorySeparatorChar);\n        path2 = path2.TrimStart(Path.AltDirectorySeparatorChar);\n    }\n\n    return Path.Combine(path1, path2);\n}\n</code></pre>\n\n<p>I also think that it is quite annoying that this string handling has to be done manually, and I\'d be interested in the reason behind this.</p>\n'}, {'answer_id': 41615722, 'author': 'marsze', 'author_id': 2060966, 'author_profile': 'https://Stackoverflow.com/users/2060966', 'pm_score': 2, 'selected': False, 'text': '<p>This actually makes sense, in some way, considering how (relative) paths are treated usually:</p>\n\n<pre><code>string GetFullPath(string path)\n{\n     string baseDir = @"C:\\Users\\Foo.Bar";\n     return Path.Combine(baseDir, path);\n}\n\n// Get full path for RELATIVE file path\nGetFullPath("file.txt"); // = C:\\Users\\Foo.Bar\\file.txt\n\n// Get full path for ROOTED file path\nGetFullPath(@"C:\\Temp\\file.txt"); // = C:\\Temp\\file.txt\n</code></pre>\n\n<p>The real question is: Why are paths, which start with <code>"\\"</code>, considered "rooted"? This was new to me too, but <a href="https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/" rel="nofollow noreferrer">it works that way on Windows</a>:</p>\n\n<pre><code>new FileInfo("\\windows"); // FullName = C:\\Windows, Exists = True\nnew FileInfo("windows"); // FullName = C:\\Users\\Foo.Bar\\Windows, Exists = False\n</code></pre>\n'}, {'answer_id': 43507984, 'author': 'ergohack', 'author_id': 4151626, 'author_profile': 'https://Stackoverflow.com/users/4151626', 'pm_score': 3, 'selected': False, 'text': '<p>Following <a href="https://www.blogger.com/profile/13444285493681626756" rel="noreferrer" title="Christian Graus">Christian Graus</a>\' advice in his "Things I Hate about Microsoft" blog titled "<a href="http://thingsihateaboutmicrosoft.blogspot.com/2009/08/pathcombine-is-essentially-useless.html" rel="noreferrer" title="Path.Combine is essentially useless.">Path.Combine is essentially useless.</a>", here is my solution:</p>\n\n<pre><code>public static class Pathy\n{\n    public static string Combine(string path1, string path2)\n    {\n        if (path1 == null) return path2\n        else if (path2 == null) return path1\n        else return path1.Trim().TrimEnd(System.IO.Path.DirectorySeparatorChar)\n           + System.IO.Path.DirectorySeparatorChar\n           + path2.Trim().TrimStart(System.IO.Path.DirectorySeparatorChar);\n    }\n\n    public static string Combine(string path1, string path2, string path3)\n    {\n        return Combine(Combine(path1, path2), path3);\n    }\n}\n</code></pre>\n\n<p>Some advise that the namespaces should collide, ...  I went with <code>Pathy</code>, as a slight, and to avoid namespace collision with <code>System.IO.Path</code>.</p>\n\n<p><strong>Edit</strong>: Added null parameter checks</p>\n'}, {'answer_id': 45443974, 'author': 'Don Rolling', 'author_id': 441862, 'author_profile': 'https://Stackoverflow.com/users/441862', 'pm_score': 0, 'selected': False, 'text': '<p>These two methods should save you from accidentally joining two strings that both have the delimiter in them.</p>\n\n<pre><code>    public static string Combine(string x, string y, char delimiter) {\n        return $"{ x.TrimEnd(delimiter) }{ delimiter }{ y.TrimStart(delimiter) }";\n    }\n\n    public static string Combine(string[] xs, char delimiter) {\n        if (xs.Length &lt; 1) return string.Empty;\n        if (xs.Length == 1) return xs[0];\n        var x = Combine(xs[0], xs[1], delimiter);\n        if (xs.Length == 2) return x;\n        var ys = new List&lt;string&gt;();\n        ys.Add(x);\n        ys.AddRange(xs.Skip(2).ToList());\n        return Combine(ys.ToArray(), delimiter);\n    }\n</code></pre>\n'}, {'answer_id': 50339209, 'author': 'Arad', 'author_id': 7734384, 'author_profile': 'https://Stackoverflow.com/users/7734384', 'pm_score': 3, 'selected': False, 'text': '<p><strong>Reason:</strong></p>\n<p>Your second URL is considered an absolute path, and the <code>Combine</code> method will only return the last path if the last path is an absolute path.</p>\n<p><strong>Solution:</strong></p>\n<p>Just remove the leading slash <code>/</code> from your second Path (<code>/SecondPath</code> to <code>SecondPath</code>), and it would work as excepted.</p>\n'}, {'answer_id': 57914892, 'author': 'shanmuga raja', 'author_id': 6581571, 'author_profile': 'https://Stackoverflow.com/users/6581571', 'pm_score': 1, 'selected': False, 'text': "<p>Remove the starting slash ('\\') in the second parameter (path2) of Path.Combine.</p>\n"}, {'answer_id': 58023547, 'author': 'LazZiya', 'author_id': 5519026, 'author_profile': 'https://Stackoverflow.com/users/5519026', 'pm_score': 2, 'selected': False, 'text': '<p>I used aggregate function to force paths combine as below:</p>\n\n<pre class="lang-cs prettyprint-override"><code>public class MyPath    \n{\n    public static string ForceCombine(params string[] paths)\n    {\n        return paths.Aggregate((x, y) =&gt; Path.Combine(x, y.TrimStart(\'\\\\\')));\n    }\n}\n</code></pre>\n'}, {'answer_id': 59379466, 'author': 'ilias iliadis', 'author_id': 2362556, 'author_profile': 'https://Stackoverflow.com/users/2362556', 'pm_score': 0, 'selected': False, 'text': '<p>As mentiond by Ryan it\'s doing exactly what the documentation says.</p>\n\n<p>From DOS times, current disk, and current path are distinguished.\n<code>\\</code> is the root path, but for the CURRENT DISK.</p>\n\n<p>For every "<em>disk</em>" there is a separate "<em>current path</em>".\nIf you change the disk using <code>cd D:</code> you do not change the current path to <code>D:\\</code>, but to: "D:\\whatever\\was\\the\\last\\path\\accessed\\on\\this\\disk"...</p>\n\n<p>So, in windows, a literal <code>@"\\x"</code> means: "CURRENTDISK:\\x".\nHence <code>Path.Combine(@"C:\\x", @"\\y")</code> has as second parameter a root path, not a relative, though not in a known disk...\nAnd since it is not known which might be the «current disk», python returns <code>"\\\\y"</code>.</p>\n\n<pre><code>&gt;cd C:\n&gt;cd \\mydironC\\apath\n&gt;cd D:\n&gt;cd \\mydironD\\bpath\n&gt;cd C:\n&gt;cd\n&gt;C:\\mydironC\\apath\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/53102', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/3798/']