È possibile esplodere un array in modo che i suoi elementi possano essere passati a un metodo con la parola chiave params?
-
08-07-2019 - |
Domanda
Prendi questo codice non compilante per esempio:
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;
}
Questo esempio è una versione in qualche modo semplificata del codice di test che sto usando. Per favore, sono interessato solo alle soluzioni che hanno a che fare direttamente con la parola chiave param. So come funzionano le liste e altre cose simili.
C'è un modo per " esplodere " l'array extraFolders in modo che il suo contenuto possa essere passato in AppendFolders insieme ad altri parametri?
Soluzione
Un'opzione è di rendere il params
parametro un 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('\\')));
}
Se vuoi qualcosa di più fortemente tipizzato, un'altra opzione è quella di creare un tipo di unione personalizzato con operatori di conversione impliciti:
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 entrambi i casi, questo verrà compilato :
appendFolders("base", "v1", "module", new[]{"debug","bin"});
Altri suggerimenti
Passalo e basta. Il parametro cartelle è innanzitutto un array. il " params " la funzionalità è un po 'magica del compilatore, ma non è richiesta.
AppendFolders(extraFolders);
Ora, in questa particolare istanza, prima devi aggiungere alcune cose a quell'array.
List<string> lstFolders = new List<string>(extraFolders);
lstFolder.Insert(0, callingModule);
lstFolder.Insert(0, version);
lstFolder.Insert(0, basefolder);
return AppendFolders(lstFolders.ToArray());
Scontrerò con il termine " collapse " ;, poiché sembra che tu voglia davvero " espandere " ;. E non sono sicuro di cosa intendi con soluzioni & Quot; avendo a che fare direttamente con la parola chiave params & Quot; e quel " non ti interessano soluzioni alternative " ;. Alla fine, devi passare un certo numero di stringhe - che il compilatore impacchetterà magicamente in un array - o direttamente un array di stringhe. Detto questo, la mia soluzione (senza cambiare l'interfaccia) sarebbe simile a:
return AppendFolders(new string[] { basefolder, version, callingModule }.Concat(extraFolders).ToArray());
Modifica:
Sebbene non sia possibile aggiungere un operatore tramite metodi di estensione, è possibile eseguire:
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();
}
Ma se andiamo così lontano - potrebbe anche estendere la Lista < T > per gestirlo elegantemente:
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();
}
}
Una soluzione rapida e sporca sarebbe quella di costruire un Elenco < string > dagli elementi e quindi passalo (con ToArray ()).
Nota che non è necessario testare la barra rovesciata. Path.Combine gestisce le cose sporche piuttosto bene .
Penso che la risposta di OregonGhost sia probabilmente il modo in cui vuoi andare. Solo per approfondire, sta suggerendo di fare qualcosa del genere:
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());
}
E non intendo questo come una lezione su come usare gli Elenchi, ma solo un piccolo chiarimento per chiunque possa venire a cercare la soluzione in futuro.