2013-08-05 48 views
10

在C#中,可以使用params关键字指定类型参数的任意数量的方法:优雅传递列表和对象PARAMS

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

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

如果你已经有对象的列表,你可以打开它进入一个数组传递给此方法:

DoStuff(fooList.ToArray()); 

但是,有没有什么优雅的方式混合n匹配?也就是说,要传递多个对象和对象列表,并将结果展平成一个列表或数组给你?理想情况下,我想能够调用我的方法是这样的:

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

至于现在,我知道如何做到这一点的唯一方法是预先处理一切到一个列表,我不不知道有什么办法可以做到这一点。

编辑:要清楚,我没有结婚到params关键字,它只是一个相关的机制,帮我解释一下我想做的事。我很满意任何看起来很干净的解决方案,并将所有东西都放到一个列表中。

+2

除非确实需要,否则不要使用'params'。 –

+1

我希望*没有办法混合和匹配。编译器如何知道你是否想要压扁一个对象数组的对象?它是否应该展平一组对象数组?它怎么可能决定何时扁平化以及何时不扁平化? –

+0

我并不十分清楚你的目标或动机是什么,但_params_的文档http://msdn.microsoft.com/en-us/library/w5zay9db.aspx显示了_object_类型数组的使用在'公共静态无效UseParams2(params对象[]列表)' –

回答

9

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

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(); 
    // 
} 
+1

+1。这很聪明,作为OP提供的难题的答案:)尽管我很确定它也是“邪恶的”:)(在Jon Skeet的意思中,例如在这个谈话中:http://youtu.be/ lGbQiguuUGc)。我不认为它应该进入生产代码,但它很好 –

+1

啊,这是我正在寻找的答案。不是因为我想在生产中使用它,而是因为我知道这个问题的聪明答案会教会我一些新的东西。非常好,谢谢。 – ean5533

4

您可以使用Enumerable.Concat加入多个列表和物品,如:

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

注:也有可能更可读的方式来实现任何你正在尝试做的。即使传球IEnumerable<Foo>更具可读性。

0

使用重载:

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[] { }); 
    } 
+0

这不会处理我想在同一个调用中传递列表和非列表的情况。 – ean5533

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; 
    } 
} 
+0

不错的解决方案。给调用者增加一点负担(调用Listify)但不是不合理的数额。 – ean5533

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); 
+0

这并不比创建一个'List '并使用'Add'和'AddRange'更清洁。 – ean5533