Можно ли взорвать массив так, чтобы его элементы могли быть переданы методу с ключевым словом params?
-
08-07-2019 - |
Вопрос
Возьмите этот некомпилируемый код, например:
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;
}
Этот пример является несколько упрощенной версией кода тестирования, который я использую. Пожалуйста, меня интересуют только решения, имеющие непосредственное отношение к ключевому слову param. Я знаю, как работают списки и другие подобные вещи.
Есть ли способ & взорвать " массив extraFolders, чтобы его содержимое могло быть передано в AppendFolders вместе с другими параметрами?
Решение
Один из вариантов - сделать параметр params
параметром 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('\\')));
}
Если вы хотите что-то более строго типизированное, другой вариант - создать пользовательский тип объединения с неявными операторами преобразования:
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};}
}
В любом случае этот будет скомпилирован:
appendFolders("base", "v1", "module", new[]{"debug","bin"});
Другие советы
Просто передайте это. Параметр folder сначала является массивом. " params " функциональность - это немного волшебства компилятора, но это не обязательно.
AppendFolders(extraFolders);
Теперь, в этом конкретном экземпляре, вам сначала нужно добавить некоторые вещи в этот массив.
List<string> lstFolders = new List<string>(extraFolders);
lstFolder.Insert(0, callingModule);
lstFolder.Insert(0, version);
lstFolder.Insert(0, basefolder);
return AppendFolders(lstFolders.ToArray());
Я буду спорить с термином " collapse " ;, поскольку кажется, что вы действительно хотите " развернуть " ;. И я не уверен, что вы имеете в виду под решениями & Quot, имеющими непосредственное отношение к ключевому слову params & Quot; и это " вас не интересуют обходные пути " ;. В конце концов, вам нужно либо передать несколько строк, которые компилятор волшебным образом упакует в массив, либо непосредственно в массив строк. При этом мое решение (без изменения интерфейса) будет выглядеть примерно так:
return AppendFolders(new string[] { basefolder, version, callingModule }.Concat(extraFolders).ToArray());
Изменить.
Хотя вы не можете добавить оператор с помощью методов расширения, вы можете сделать следующее:
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();
}
Но, если мы пойдем так далеко - можно просто расширить List < T > чтобы справиться с этим элегантно:
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();
}
}
Быстрое и грязное решение - создать список < string > из элементов, а затем передать это (с ToArray ()).
Обратите внимание, что вам не нужно проверять обратную косую черту. Path.Combine обрабатывает грязные вещи довольно хорошо . р>
Я думаю, что ответом OregonGhost, вероятно, будет то, что вы хотите. Просто чтобы уточнить это, он предлагает сделать что-то вроде этого:
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());
}
И я не имею в виду это как урок о том, как использовать списки, просто как небольшое разъяснение для тех, кто может прийти к поиску решения в будущем.