C# Tip: Path.Combine and Path.Join are similar but way different.
Just a second! π«·
If you are here, it means that you are a software developer. So, you know that storage, networking, and domain management have a cost .
If you want to support this blog, please ensure that you have disabled the adblocker for this site. I configured Google AdSense to show as few ADS as possible - I don't want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.
Thank you for your understanding.
- Davide
When you need to compose the path to a folder or file location, you can rely on the Path
class. It provides several static methods to create, analyze and modify strings that represent a file system.
Path.Join
and Path.Combine
look similar, yet they have some important differences that you should know to get the result you are expecting.
Path.Combine: take from the last absolute path
Path.Combine
concatenates several strings into a single string that represents a file path.
Path.Combine("C:", "users", "davide");
// C:\users\davide
However, there’s a tricky behaviour: if any argument other than the first contains an absolute path, all the previous parts are discarded, and the returned string starts with the last absolute path:
Path.Combine("foo", "C:bar", "baz");
// C:bar\baz
Path.Combine("foo", "C:bar", "baz", "D:we", "ranl");
// D:we\ranl
Path.Join: take everything
Path.Join
does not try to return an absolute path, but it just joins the string using the OS path separator:
Path.Join("C:", "users", "davide");
// C:\users\davide
This means that if there is an absolute path in any argument position, all the previous parts are not discarded:
Path.Join("foo", "C:bar", "baz");
// foo\C:bar\baz
Path.Join("foo", "C:bar", "baz", "D:we", "ranl");
// foo\C:bar\baz\D:we\ranl
Final comparison
As you can see, the behaviour is slightly different.
Let’s see a table where we call the two methods using the same input strings:
Path.Combine | Path.Join | |
---|---|---|
["singlestring"] |
singlestring |
singlestring |
["foo", "bar", "baz"] |
foo\bar\baz |
foo\bar\baz |
["foo", " bar ", "baz"] |
foo\ bar \baz |
foo\ bar \baz |
["C:", "users", "davide"] |
C:\users\davide |
C:\users\davide |
["foo", " ", "baz"] |
foo\ \baz |
foo\ \baz |
["foo", "C:bar", "baz"] |
C:bar\baz |
foo\C:bar\baz |
["foo", "C:bar", "baz", "D:we", "ranl"] |
D:we\ranl |
foo\C:bar\baz\D:we\ranl |
["C:", "/users", "/davide"] |
/davide |
C:/users/davide |
["C:", "users/", "/davide"] |
/davide |
C:\users//davide |
["C:", "\users", "\davide"] |
\davide |
C:\users\davide |
Have a look at some specific cases:
- neither methods handle white and empty spaces:
["foo", " ", "baz"]
are transformed tofoo\ \baz
. Similarly,["foo", " bar ", "baz"]
are combined intofoo\ bar \baz
, without removing the head and trail whitespaces. So, always remove white spaces and empty values! Path.Join
handles in a not-so-obvious way the case of a path starting with/
or\
: if a part starts with\
, it is included in the final path; if it starts with/
, it is escaped as//
. This behaviour depends on the path separator used by the OS: in my case, I’m running these methods using Windows 11.
Finally, always remember that the path separator depends on the Operating System that is running the code. Don’t assume that it will always be /
: this assumption may be correct for one OS but wrong for another one.
This article first appeared on Code4IT π§
Wrapping up
As we have learned, Path.Combine
and Path.Join
look similar but have profound differences.
Dealing with path building may look easy, but it hides some complexity. Always remember to:
- validate and clean your input before using either of these methods (remove empty values, white spaces, and head or trailing path separators);
- always write some Unit Tests to cover all the necessary cases;
I hope you enjoyed this article! Let’s keep in touch on Twitter or LinkedIn! π€π€
Happy coding!
π§