views:

677

answers:

4

I'm surprised there's not an overload that can take a string array. Anyway, what is the best way to avoid nesting calls to Path.Combine?

pathValue = Path.Combine(path1, Path.Combine(path2, Path.Combine(path3, path4)))

This seems inefficient since it results in 4 new strings being created just to get 1.

A: 

System.IO.Path.Combine takes two arguments because that's how they designed it.

That may be due to the fact that two arguments is the most common use case, and perhaps also because with two arguments, you can compose multiple Combine calls to create the effect of having more than two arguments.

However, since I was not involved in the design of this method, nor have ever worked at Microsoft, I can't say that I actually know why they decided to design it this way.

John Saunders
+10  A: 

The efficiency side of things isn't the problem IMO - it's the usability side of things. Personally I think there ought to be an overload of:

Combine(string first, string second, string third, params string[] others)

You need to have at least three to prevent it from clashing with the existing two-parameter version if you just write Path.Combine("foo", "bar") but it would certainly help to make code clearer. Why not open a feature request on Connect?

Of course, you can implement this yourself (and in another class the number of parameters doesn't matter so much):

public static string CombinePaths(string first, params string[] others)
{
    // Put error checking in here :)
    string path = first;
    foreach (string section in others)
    {
        path = Path.Combine(path, section);
    }
    return path;
}
Jon Skeet
Downvoters: please give a reason, otherwise your downvote is pointless.
Jon Skeet
the OP's wish has been granted. Support for Path.Combine(params) has been added in .net 4 Beta2 - http://blogs.msdn.com/bclteam/archive/2009/10/21/what-s-new-in-the-bcl-in-net-4-beta-2-justin-van-patten.aspx
Gishu
+1  A: 

It's pretty straightforward to implement it yourself:

public string Combine(params string[] paths)
{
    char[] pathSeparators = new char[] 
        { Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar, Path.VolumeSeparatorChar };

    if(paths == null) throw new ArgumentNullException("paths");
    if(paths.Length == 1) return paths[0];

    StringBuilder pathBuilder = new StringBuilder();

    foreach(string path in paths)
    {
        if(Path.IsPathRooted(path))
            pathBuilder = new StringBuilder(path);
        else
        {
            char last = pathBuilder.Length > 0 ?
                pathBuilder[pathBuilder.Length - 1] :
                path[path.Length - 1];

            if(Array.IndexOf(pathSeparators, last) == -1)
                pathBuilder.Append(Path.DirectorySeparatorChar);

            pathBuilder.Append(path);
        } // else
    } // foreach

    return pathBuilder.ToString();
}

[Test()]
public void CombinePaths()
{
    string result = Combine(@"C:\Program Files\", @"Microsoft.NET", @"ADOMD.NET\", "90", "msadomdx.dll");
    Assert.AreEqual(@"C:\Program Files\Microsoft.NET\ADOMD.NET\90\msadomdx.dll", result);
}
Anton Gogolev
+12  A: 

If you already have an array or an IEnumerable then you could do this in one line...

// I'm assuming that you've got an array or IEnumerable<T> from somewhere
var paths = new string[] { path1, path2, path3, path4, path5, path6 };

string result = paths.Aggregate(Path.Combine);

If not, then how about writing your own extension method to string...

public static class PathExtension
{
    public static string CombinePathWith(this string path1, string path2)
    {
        return Path.Combine(path1, path2);
    }
}

... that would allow you to chain these like this...

string result = path1.CombinePathWith(path2)
                     .CombinePathWith(path3)
                     .CombinePathWith(path4)
                     .CombinePathWith(path5)
                     .CombinePathWith(path6);
Martin Peck
Cool, I didn't know about Enumerable.Aggregate!
Christian Hayter
@Christian. Aggregate is a very powerful tool to have at hand.
Martin Peck