Элегантно передавая списки и объекты в качестве параметров
В С# можно использовать ключевое слово params
, чтобы указать произвольное количество типизированных параметров для метода:
public void DoStuff(params Foo[] foos) {...}
public void OtherStuff {
DoStuff(foo1);
DoStuff(foo2, foo3);
}
Если у вас уже есть список объектов, вы можете превратить его в массив, чтобы перейти к этому методу:
DoStuff(fooList.ToArray());
Однако есть ли какой-нибудь изящный способ mix-n-match? То есть, передавать в нескольких объектах и списках объектов и иметь результаты, сглаженные в один список или массив для вас? В идеале, я хотел бы иметь возможность вызвать мой метод следующим образом:
DoStuff(fooList, foo1, foo2, anotherFooList, ...);
Как сейчас, единственный способ, которым я знаю, как это сделать, - предварительно обработать все в один список, и я вообще не знаю, как это сделать в целом.
Изменить: Чтобы быть ясным, я не женат на ключевом слове params
, это просто связанный механизм, который помог мне объяснить, что я хотел сделать. Я очень доволен любым решением, которое выглядит чистым и сглаживает все в один список.
Ответы
Ответ 1
Вы можете создать класс с implict-конверсиями для обертывания одного элемента и списка:
public class ParamsWrapper<T> : IEnumerable<T>
{
private readonly IEnumerable<T> seq;
public ParamsWrapper(IEnumerable<T> seq)
{
this.seq = seq;
}
public static implicit operator ParamsWrapper<T>(T instance)
{
return new ParamsWrapper<T>(new[] { instance });
}
public static implicit operator ParamsWrapper<T>(List<T> seq)
{
return new ParamsWrapper<T>(seq);
}
public IEnumerator<T> GetEnumerator()
{
return this.seq.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
то вы можете изменить свой метод DoStuff
на:
private static void DoStuff(params ParamsWrapper<Foo>[] foos)
{
Foo[] all = foos.SelectMany(f => f).ToArray();
//
}
Ответ 2
Вы можете использовать Enumerable.Concat для объединения нескольких списков и элементов, таких как:
DoStuff(fooList
.Concat(Enumerable.Repeat(foo1,1))
.Concat(Enumerable.Repeat(foo2,1))
.Concat(Enumerable.Repeat(anotherFooList))
.ToArray();
Примечание: есть, вероятно, гораздо более читаемые способы достижения того, что вы пытаетесь сделать. Даже прохождение IEnumerable<Foo>
более читаемо.
Ответ 3
Вы не можете делать то, что вы пытаетесь сделать, но с помощью метода расширения вы можете получить довольно близко:
void Main()
{
var singleFoo = new Foo();
var multipleFoos = new[] { new Foo(), new Foo(), new Foo() };
var count = DoStuffWithFoos(singleFoo.Listify(), multipleFoos).Count();
Console.WriteLine("Total Foos: " + count.ToString());
}
public IEnumerable<Foo> DoStuffWithFoos(params IEnumerable<Foo>[] fooLists)
{
return fooLists.SelectMany(fl => fl); // this flattens all your fooLists into
// a single list of Foos
}
public class Foo { }
public static class ExtensionMethods
{
public static IEnumerable<Foo> Listify(this Foo foo)
{
yield return foo;
}
}
Ответ 4
Использовать перегрузки:
public void DoStuff(Foo foo)
{
//Do some stuff here with foo
}
public void DoStuff(params Foo[] foos)
{
foreach (var foo in foos)
{
DoStuff(foo);
}
}
public void DoStuff(params IEnumerable<Foo>[] foos)
{
foreach (var items in foos)
{
DoStuff(items);
}
}
public void OtherStuff()
{
DoStuff(new Foo());
DoStuff(new Foo(), new Foo());
DoStuff(new Foo[] { });
DoStuff(new Foo[] { }, new Foo[] { });
}
Ответ 5
вы можете сделать отдельные методы для загрузки объектов в одну коллекцию, а не изящную, но она будет работать, а логика действительно легко следовать и не очень сложно реализовать.
public class Flattener<T> : IEnumerable<T>
{
private List<T> _collection = new List<T> ( );
public void Add ( params T [ ] list )
{
_collection.AddRange ( list );
}
public void Add ( params IEnumerable<T> [ ] lists )
{
foreach ( var list in lists )
_collection.AddRange ( list );
}
public T Result
{
get
{
return _collection.ToArray();
}
}
public IEnumerator<T> GetEnumerator ( )
{
return _collection.GetEnumerator ( );
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ( )
{
return GetEnumerator ( );
}
}
Flattener<Foo> foos = new Flattener();
foos.Add(fooList, fooList2, fooList3,...);
foos.Add(foo1,foo2,foo3,...);
DoStuff(foos.Result);