2013-08-05 6 views
10

In C# vorbei, kann man das params Schlüsselwort verwenden, um eine beliebige Anzahl von typisierte Parameter an eine Methode definieren:Elegantly Listen und Objekte als params

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

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

Wenn Sie bereits eine Liste von Objekten haben, können Sie drehen es in ein Array, um diese Methode zu übergeben:

DoStuff(fooList.ToArray()); 

Gibt es jedoch eine elegante Möglichkeit zum Mix-n-Match? Das heißt, mehrere Objekte und Listen von Objekten zu übergeben und die Ergebnisse für Sie in eine Liste oder ein Array zu verflachen? Im Idealfall würde ich so meine Methode können gerne anrufen:

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

Ab jetzt, ich der einzige Weg, weiß, wie dies zu tun ist, alles in eine Liste zu-Prozesses vor, und ich don‘ Ich kenne keine Möglichkeit, dies generisch zu tun.

Edit: klar sein, ich bin nicht mit dem Stichwort params verheiratet, es ist nur ein verwandter Mechanismus, der half mir erklären, was ich tun wollte. Ich bin ziemlich glücklich mit jeder Lösung, die sauber aussieht und alles in einer einzigen Liste abbildet.

+2

nicht 'params' verwenden, außer es wird wirklich benötigt. –

+1

Ich hoffe, es gibt keine Möglichkeit zu kombinieren. Wie würde der Compiler wissen, ob Sie ein Objekt, das ein Array von Objekten war, abflachen wollten? Sollte es eine Reihe von Arrays von Objekten abflachen? Wie könnte es sich entscheiden, wann und wo nicht? –

+0

Ich bin nicht ganz klar, was Ihr Ziel oder Ihre Motivation ist, aber die Dokumentation für _params_ http://msdn.microsoft.com/en-us/library/w5zay9db.aspx zeigt die Verwendung eines Arrays vom Typ _object_ als in 'public static void UseParams2 (params object [] Liste)' –

Antwort

9

Sie eine Klasse mit implict Konvertierungen schaffen könnte ein einzelnes Element und eine Liste zu wickeln:

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

dann können Sie Ihre DoStuff Methode ändern:

private static void DoStuff(params ParamsWrapper<Foo>[] foos) 
{ 
    Foo[] all = foos.SelectMany(f => f).ToArray(); 
    // 
} 
+1

+1. Das ist schlau, als Antwort auf das Puzzle, das das OP vorstellt :) Obwohl ich mir ziemlich sicher bin, dass es auch "böse" ist :) (in dem Sinne, dass Jon Skeet meint, zB in diesem Vortrag: http://youtu.be/ lGbQiguuUGc). Ich denke nicht, dass es in Produktionscode gehen sollte, aber es ist nett –

+1

Ah, das ist die Art von Antwort, die ich suchte.Nicht weil ich das in der Produktion nutzen möchte, sondern weil ich wusste, dass kluge Antworten auf dieses Problem mir etwas Neues beibringen würden. Großartig, danke. – ean5533

4

können Sie Enumerable.Concat verwenden, um mehrere Listen und Elemente wie beitreten:

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

Hinweis: es wahrscheinlich viel besser lesbar Möglichkeiten, zu erreichen, was Sie zu tun versuchen. Sogar die Übergabe IEnumerable<Foo> ist besser lesbar.

0

Verwendung Überlastungen:

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

Dies würde nicht den Fall behandeln, wo ich eine Liste und eine Nicht-Liste in dem gleichen Anruf übergeben möchte. – ean5533

1

Sie können nicht ganz das tun, was Sie versuchen, aber mit einer Erweiterungsmethode, Sie könnte ziemlich nahe kommen:

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

Keine schlechte Lösung. Fügt dem Aufrufer ein kleines bisschen Last hinzu (um Listify aufzurufen), aber keine unangemessene Menge. – ean5533

0

können Sie verschiedene Methoden machen die Objekte in derselben Sammlung zu laden, nicht elegant, aber es wird funktionieren, und die Logik ist wirklich einfach zu folgen, und nicht sehr schwer zu implementieren .

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

Das ist nicht viel sauberer, als nur eine 'Liste ' zu erstellen und 'Add' und' AddRange' zu ​​verwenden. – ean5533

Verwandte Themen