14

在 C# 中,可以使用params关键字为方法指定任意数量的类型参数:

public void DoStuff(params Foo[] foos) {...}

public void OtherStuff {
    DoStuff(foo1);
    DoStuff(foo2, foo3);
}

如果你已经有一个对象列表,你可以把它变成一个数组传递给这个方法:

DoStuff(fooList.ToArray());

但是,有没有什么优雅的方式来混搭?也就是说,要传入多个对象和对象列表并将结果扁平化为一个列表或数组?理想情况下,我希望能够像这样调用我的方法:

DoStuff(fooList, foo1, foo2, anotherFooList, ...);

截至目前,我知道如何做到这一点的唯一方法是将所有内容预处理到一个列表中,我不知道有任何方法可以通用地做到这一点。

编辑:要清楚,我没有嫁给params关键字,它只是一个相关的机制,帮助我解释我想要做什么。我对任何看起来很干净并将所有内容扁平化为一个列表的解决方案感到非常满意。

4

4 回答 4

13

您可以创建一个具有隐式转换的类来包装单个元素和一个列表:

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();
    //
}
于 2013-08-05T19:58:39.550 回答
4

您可以使用Enumerable.Concat加入多个列表和项目,例如:

 DoStuff(fooList
       .Concat(Enumerable.Repeat(foo1,1))
       .Concat(Enumerable.Repeat(foo2,1))
       .Concat(Enumerable.Repeat(anotherFooList))
       .ToArray();

注意:可能有更易读的方法来实现你想要做的任何事情。即使通过IEnumerable<Foo>也更具可读性。

于 2013-08-05T19:58:26.750 回答
1

你不能完全做你想做的事,但是使用扩展方法,你可以非常接近:

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;
    }
}
于 2013-08-05T19:59:54.910 回答
0

您可以创建单独的方法将对象加载到同一个集合中,虽然不是那么优雅,但它会起作用,而且逻辑真的很容易遵循,而且实现起来并不难。

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);
于 2013-08-05T20:07:44.003 回答