34

To combine two parts of a file path, you can do

System.IO.Path.Combine (path1, path2);

However, you can't do

System.IO.Path.Combine (path1, path2, path3);

Is there a simple way to do this?

Matthew
  • 28,056
  • 26
  • 104
  • 170

5 Answers5

35

Here's a utility method you can use:

public static string CombinePaths(string path1, params string[] paths)
{
    if (path1 == null)
    {
        throw new ArgumentNullException("path1");
    }
    if (paths == null)
    {
        throw new ArgumentNullException("paths");
    }
    return paths.Aggregate(path1, (acc, p) => Path.Combine(acc, p));
}

Alternate code-golf version (shorter, but not quite as clear, semantics are a bit different from Path.Combine):

public static string CombinePaths(params string[] paths)
{
    if (paths == null)
    {
        throw new ArgumentNullException("paths");
    }
    return paths.Aggregate(Path.Combine);
}

Then you can call this as:

string path = CombinePaths(path1, path2, path3);
Aaronaught
  • 120,909
  • 25
  • 266
  • 342
  • This might be a stupid question, but where does Aggregate come from? I'm using Mono targeting Mono / .Net 3.5, and the compiler can't find it. – Matthew Jan 03 '10 at 20:32
  • 2
    Ahh - `Aggregate` is an extension method in the `System.Linq` namespace, which is in `System.Core.dll`. – Aaronaught Jan 03 '10 at 20:35
  • 5
    Why the extra 'path1' parameter? Also, the last line can be shortened to `return paths.Aggregate(/*path1, */Path.Combine);` – Sebastian Ullrich Jan 03 '10 at 20:37
  • @Kha: This is true, I wrote it that way to preserve the semantics of the original `Path.Combine` and to try to indicate clearly that at least one path is required for the method to work correctly. `Path.Combine` will throw an `ArgumentNullException` if either `path1` or `path2` are `null`. But you're absolutely right, it's not totally necessary. – Aaronaught Jan 03 '10 at 20:40
  • Kha: I like that simple version better. I edited my question to include the modified utility method. – Matthew Jan 03 '10 at 20:45
  • Resharper is so nice to suggest: "Use Method Group" (second return statement vesion) – George Polevoy Jan 03 '10 at 21:48
  • I would just like to add, and this is an issue with Path.Combine, not this code, that adding a leading "\" on a non-starting parameter will ignore the previous. – gunr2171 Nov 13 '12 at 14:54
29

As others have said, in .NET 3.5 and earlier versions there hasn't been a way to do this neatly - you either have to write your own Combine method or call Path.Combine multiple times.

But rejoice - for in .NET 4.0, there is this overload:

public static string Combine(
    params string[] paths
)

There are also overloads taking 3 or 4 strings, presumably so that it doesn't need to create an array unnecessarily for common cases.

Hopefully Mono will port those overloads soon - I'm sure they'd be easy to implement and much appreciated.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Shouldn't declaring single array be more efficient then declaring 4 different variables? What could be the reason for 4 or 3 string overload? – Muhammad Hasan Khan Jan 03 '10 at 20:40
  • 1
    @Hasan: No, creating an array requires a separate object which has to be garbage collected later, etc. Passing two separate variables is more efficient than creating a new array containing two references. – Jon Skeet Jan 03 '10 at 20:53
  • 5
    No need to wait, it's implemented since 10/21/09 :) http://anonsvn.mono-project.com/viewvc?view=revision&revision=144597 – Sebastian Ullrich Jan 03 '10 at 21:01
  • 2
    http://msdn.microsoft.com/en-us/library/dd784047(VS.90).aspx states that Path.Combine will take 3 strings in .Net 3.5 which is clearly wrong since it gives a compiler error when used. the msdn library is wrong in this case. I put this here in case someone else lands on this page after getting this compiler error. – Maggie Jul 20 '10 at 20:45
  • Now that .NET 4.0 is out, I have changed the accepted answer to this answer. – Matthew Aug 04 '11 at 18:41
5

Not simple, but clever :)

string str1 = "aaa", str2 = "bbb", str3 = "ccc";
string comb = new string[] { str1, str2, str3 }
    .Aggregate((x, y) => System.IO.Path.Combine(x, y));

Or:

string CombinePaths(params string[] paths)
{
    return paths.Aggregate((x,y) => System.IO.Path.Combine(x, y));
}

EDIT Order23's answer is actually up to date with current .NET https://stackoverflow.com/a/41148772/235648

Aviad P.
  • 32,036
  • 14
  • 103
  • 124
  • 2
    This is handy if you're not doing it more than once. You can simplify it a lot, though: `new [] { "aaa", "bbb", "ccc" }.Aggregate (Path.Combine);` (assuming you are `using System.IO;`). – Matthew Mar 27 '11 at 19:13
4

Nope - you have to call Path.Combine() several times.

You could write a helper method that does it for you, though:

public static string CombinePaths(params string[] paths) {
    if (paths == null) {
        return null;
    }
    string currentPath = paths[0];
    for (int i = 1; i < paths.Length; i++) {
        currentPath = Path.Combine(currentPath, paths[i]);
    }
    return currentPath;
}
Eilon
  • 25,582
  • 3
  • 84
  • 102
  • 1
    Taking a look at the logic you can easily get rid of that second if statement. – ChaosPandion Jan 03 '10 at 20:26
  • 1
    @ChaosPandion: If you're going to avoid Linq then you should also optimize the Schlemiel-the-painter algorithm and instead use Path.PathSeparator and other static fields with a `StringBuilder`. I figured performance wasn't an issue here. – Aaronaught Jan 03 '10 at 20:27
  • 1
    "Unnecessary LINQ"? Perhaps you forgot to specify the [.net-2.0] tag? – Ryan Lundy Jan 03 '10 at 20:30
  • 3
    @ChaosPandion - "unnecessary Linq"? Maybe - if performance was critical. Or perhaps if only .Net 2. But actually the Linq approach is simple, concise and intentional. And if it gets people to think in functional terms it is no bad thing. – Rob Levine Jan 03 '10 at 20:34
  • I love LINQ as much as the next guy but come on for something as simple as this? You guys need to take it easy. – ChaosPandion Jan 03 '10 at 20:41
  • string strPath = ""; foreach (string str in paths) strPath = System.IO.Path.Combine(strPath, str); will do. You needn't take care of path 0. – Stefan Steiger Mar 26 '11 at 22:34
2

With the method overload introduced in .NET 4 Path.Combine(string [])

Path.Combine(new [] { "abc", "def", "ghi", "jkl", "mno" });
silentsod
  • 8,165
  • 42
  • 40
Order23
  • 71
  • 3