Reputation: 8994
In C#, one can use the params
keyword to specify an arbitrary number of typed parameters to a method:
public void DoStuff(params Foo[] foos) {...}
public void OtherStuff {
DoStuff(foo1);
DoStuff(foo2, foo3);
}
If you already have a list of objects, you can turn it into an array to pass to this method:
DoStuff(fooList.ToArray());
However, is there any elegant way to mix-n-match? That is, to pass in multiple objects and lists of objects and have the results flattened into one list or array for you? Ideally, I would like to be able to call my method like this:
DoStuff(fooList, foo1, foo2, anotherFooList, ...);
As of right now, the only way I know how to do this is to pre-process everything into one list, and I don't know of any way to do this generically.
Edit: To be clear, I'm not married to the params
keyword, it's just a related mechanism that helped me explain what I wanted to do. I'm quite happy with any solution that looks clean and flattens everything into a single list.
Upvotes: 16
Views: 35146
Reputation: 3231
you could make separate methods to load the objects into the same collection, not that elegant but it will work, and the logic is really easy to follow, and not very hard to implement.
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);
Upvotes: 1
Reputation: 42344
You can't quite do what you're trying to do, but with an extension method, you could get pretty close:
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;
}
}
Upvotes: 2
Reputation: 144126
You could create a class with implict conversions to wrap a single element and a list:
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();
}
}
then you can change your DoStuff
method to:
private static void DoStuff(params ParamsWrapper<Foo>[] foos)
{
Foo[] all = foos.SelectMany(f => f).ToArray();
//
}
Upvotes: 13
Reputation: 100527
You can use Enumerable.Concat to join multiple lists and items like:
DoStuff(fooList
.Concat(Enumerable.Repeat(foo1,1))
.Concat(Enumerable.Repeat(foo2,1))
.Concat(Enumerable.Repeat(anotherFooList))
.ToArray();
Note: there are likely much more readable ways to achieve whatever you are trying to do. Even passing IEnumerable<Foo>
is more readable.
Upvotes: 4