views:

585

answers:

5

Take this non-compiling code for instance:

public string GetPath(string basefolder, string[] extraFolders)
{
    string version = Versioner.GetBuildAndDotNetVersions();
    string callingModule = StackCrawler.GetCallingModuleName();
    return AppendFolders(basefolder, version, callingModule, extraFolders);
}
private string AppendFolders(params string[] folders)
{
    string outstring = folders[0];
    for (int i = 1; i < folders.Length; i++)
    {
        string fixedPath = folders[i][0] == '\\' ? folders[i].Substring(1) : folders[i];
        Path.Combine(outstring, fixedPath);
    }
    return outstring;
}

This example is a somewhat simplified version of testing code I am using. Please, I am only interested in solutions having directly to do with the param keyword. I know how lists and other similar things work.

Is there a way to "explode" the extraFolders array so that it's contents can be passed into AppendFolders along with other parameters?

+1  A: 

A quick and dirty solution would be to build a List<string> from the items and then pass that (with ToArray()).

Note that you don't need to test for the backslash. Path.Combine handles the dirty things rather fine.

OregonGhost
In some cases it won't handle the backslash properly. I have ended up with a double backslash in the past. ex. Blah\\Blah
Rick Minerich
Then it might be a bug which you can file on http://connect.microsoft.com - and if you want to stay with doing the backslash thing, at least use Path.DirectorySeparatorChar instead of a hard-coded backslash.
OregonGhost
+6  A: 

Just pass it. The folders parameter is an array first. the "params" functionality is a little bit of compiler magic, but it's not required.

AppendFolders(extraFolders);

Now, it this particulat instance, you'll have to add some things to that array, first.

List<string> lstFolders = new List<string>(extraFolders);
lstFolder.Insert(0, callingModule);
lstFolder.Insert(0, version);
lstFolder.Insert(0, basefolder);
return AppendFolders(lstFolders.ToArray());
James Curran
I guess the point was that you can't just add things to an array :)
OregonGhost
+1  A: 

I think OregonGhost's answer is probably the way you want to go. Just to elaborate on it, he's suggesting doing something like this:

public string GetPath(string basefolder, string[] extraFolders)
{
    string version = Versioner.GetBuildAndDotNetVersions();
    string callingModule = StackCrawler.GetCallingModuleName();

    List<string> parameters = new List<string>(extraFolders.Length + 3);
    parameters.Add(basefolder);
    parameters.Add(version);
    parameters.Add(callingModule);
    parameters.AddRange(extraFolders);
    return AppendFolders(parameters.ToArray());
}

And I don't mean that as a lesson on how to use Lists, just as a little clarification for anybody who may come along looking for the solution in the future.

MojoFilter
+2  A: 

I'll quibble with the term "collapse", since it seems you really want to "expand". And I'm not sure what you mean by solutions "having directly to do with params keyword" and that "you're not interested in workarounds". In the end, you either have to pass a number of strings - which the compiler will magically package into an array - or an array of strings directly. That being said, my solution (without changing the interface) would go something like:

return AppendFolders(new string[] { basefolder, version, callingModule }.Concat(extraFolders).ToArray());

Edit:

While you can't add an operator via extension methods, you could do:

return AppendFolders(new string[] { baseFolder, callingModuleName, version }.Concat(extraFolders));

public static T[] Concat<T>(this T[] a, T[] b) {
   return ((IEnumerable<T>)a).Concat(b).ToArray();
}

But, if we're going to go that far - might as well just extend List<T> to handle this elegantly:

return AppendFolders(new Params<string>() { baseFolder, callingModuleName, version, extraFolders });

class Params<T> : List<T> {
    public void Add(IEnumerable<T> collection) {
       base.AddRange(collection);
    }

    public static implicit operator T[](Params<T> a) {
       return a.ToArray();
    }
}
Mark Brackett
This is really close to what I want. Elegant and similar to what the a compiler might do.
Rick Minerich
Equally cool would be: new string[] { baseFolder, callingModuleName, version } + extraFolders
Rick Minerich
+1  A: 

One option is to make the params parameter an object[]:

static string appendFolders(params object[] folders)
 { return (string) folders.Aggregate("",(output, f) => 
                       Path.Combine( (string)output
                                    ,(f is string[]) 
                                      ? appendFolders((object[])f)
                                      : ((string)f).TrimStart('\\')));
 }

If you want something more strongly-typed, another option is to create a custom union type with implicit conversion operators:

  static string appendFolders(params StringOrArray[] folders)
     { return folders.SelectMany(x=>x.AsEnumerable())
                     .Aggregate("",
                       (output, f)=>Path.Combine(output,f.TrimStart('\\')));
     }

   class StringOrArray
     { string[] array;

       public IEnumerable<string> AsEnumerable()
        { return soa.array;}

       public static implicit operator StringOrArray(string   s)   
        { return new StringOrArray{array=new[]{s}};}

       public static implicit operator StringOrArray(string[] s)  
        { return new StringOrArray{array=s};}
     }

In either case, this will compile:

appendFolders("base", "v1", "module", new[]{"debug","bin"});
Mark Cidade