Элегантно передавая списки и объекты в качестве параметров

В С# можно использовать ключевое слово 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);