2009-08-23 9 views
24

Ich bin auf der Suche nach einem Weg, um eine Funktion zu haben, wie:Key Wert-Paare in C# Params

myFunction({"Key", value}, {"Key2", value}); 

Ich bin sicher, dass etwas mit anonymen Typen gibt es die ziemlich einfach sein würden, aber ich bin nicht es sehen.

Die einzige Lösung, die ich denken kann, ist ein „params KeyValuePair [] Paare“ Parameter zu haben, aber das endet etwas ähnliches zu sein:

myFunction(new KeyValuePair<String, object>("Key", value), new KeyValuePair<String, object>("Key2", value)); 

Welche zugegebenermaßen ist, viel hässlicher.

EDIT:

Um zu klären, ich bin eine „Message“ Klasse Schreiben zwischen zwei verschiedenen Systemen zu übergeben. Es enthält eine Nachricht, die den Nachrichtentyp angibt, und ein Wörterbuch der zu bearbeitenden Zeichenfolge für "Daten", die der Nachricht zugeordnet sind. Ich möchte in der Lage sein, all diese Informationen im Konstruktor zu übergeben, also kann ich dies tun:

Agent.SendMessage (neue Nachricht (MessageTypes.SomethingHappened, "A", x, "B", y , "C", z)); oder ähnliche Syntax.

+0

ich meine Antwort bearbeitet eine angemessene Art und Weise zu schließen, das Wörterbuch-Syntax zu verwenden. Achten Sie darauf, das zu überprüfen. –

Antwort

52

Wenn die Syntax für ein ansonsten gutes Muster schlecht ist, ändern Sie die Syntax.Wie wäre es:

public void MyFunction(params KeyValuePair<string, object>[] pairs) 
{ 
    // ... 
} 

public static class Pairing 
{ 
    public static KeyValuePair<string, object> Of(string key, object value) 
    { 
     return new KeyValuePair<string, object>(key, value); 
    } 
} 

Verbrauch:

MyFunction(Pairing.Of("Key1", 5), Pairing.Of("Key2", someObject)); 

Noch interessanter eine Erweiterungsmethode zu string hinzuzufügen wäre es paarbar zu machen:

public static KeyValuePair<string, object> PairedWith(this string key, object value) 
{ 
    return new KeyValuePair<string, object>(key, value); 
} 

Verbrauch:

MyFunction("Key1".PairedWith(5), "Key2".PairedWith(someObject)); 

bearbeiten: Sie können auch das Wörterbuch Syntax ohne die generischen Klammern verwenden, indem von Dictionary<,> Ableitung:

public void MyFunction(MessageArgs args) 
{ 
    // ... 
} 

public class MessageArgs : Dictionary<string, object> 
{} 

Verbrauch:

MyFunction(new MessageArgs { { "Key1", 5 }, { "Key2", someObject } }); 
+0

Hmm, das ist eine clevere Lösung, ich mag es. Ich denke, das ist, was ich verwenden werde. Vielen Dank. Ich werde die Frage für ein oder zwei Stunden unbeantwortet lassen, um zu sehen, ob noch andere Lösungen auftauchen. – Quantumplation

+0

Oder Sie könnten die Boxroute gehen und 'MyFunction (params object [] args)' –

2

Mit einem Wörterbuch:

myFunction(new Dictionary<string, object>(){ 
    {"Key", value}, 
    {"Key2", value}}); 

Welche geradlinig ist, müssen Sie nur eine new Dictionary<K, V>, nicht für jedes Argument. Es ist trivial, die Schlüssel und Werte zu bekommen.

Oder mit einem anonymen Typ:

myFunction(new { 
    Key = value, 
    Key2 = value}); 

, die nicht sehr schön ist innerhalb der Funktion zu nutzen, müssen Sie Reflexion. Dies würde das ungefähr so ​​aussehen:

foreach (PropertyInfo property in arg.GetType().GetProperties()) 
{ 
    key = property.Name; 
    value = property.GetValue(arg, null); 
} 

(Staight aus meinem Kopf, wahrscheinlich einige Fehler ...)

+0

Wie würde ich die Schlüssel/Wert-Paare aus dem zweiten Typ zeichnen? – Quantumplation

+0

Sie finden die Sekunde in der ActionLink-Methode in asp.net MVC. Benötigt dies eine Reflektion, um die Namen und Werte der Eigenschaften zu entfernen? –

2

Verwenden Sie ein Dictionary ...

void Main() 
{ 
    var dic = new Dictionary<string, object>(); 
    dic.Add("Key1", 1); 
    dic.Add("Key2", 2); 

    MyFunction(dic).Dump(); 
} 

public static object MyFunction(IDictionary dic) 
{ 
    return dic["Key1"]; 
} 
+1

Nun ja, aber der Aufruf der Funktion ist hier kompliziert, da vorher ein Wörterbuch deklariert werden muss. Auch für was ist .Dump()? – Quantumplation

+0

Hoppla, Dump kommt von LINQPad, denke einfach Console.Writeline. Warum solltest du dich dazu bringen, alles in einer Aussage zu haben? Sie sollten Ihre Schnittstelle korrekt machen. Sich um die Aufrufkonvention zu sorgen, sollte eine entfernte Sekunde sein. –

+2

Es ist für den Konstruktor einer Message-Klasse, etwas, das oft gesendet wird, und eine unhandliche Aufrufkonvention wird später in die Quere kommen. ein Wörterbuch deklarieren, die Schlüssel hinzufügen und es dem Konstruktor für viele verschiedene Instanzen übergeben, wo Nachrichten gesendet werden, wird schwer tolerierbar und lenkt einen Leser/Schreiber von dem, was gerade passiert, ab und wird daher schwieriger zu pflegen. – Quantumplation

2

Hier ist mehr von der gleichen:

static void Main(string[] args) 
{ 
    // http://msdn.microsoft.com/en-us/library/bb531208.aspx 
    MyMethod(new Dictionary<string,string>() 
    { 
     {"key1","value1"}, 
     {"key2","value2"} 
    }); 
} 

static void MyMethod(Dictionary<string, string> dictionary) 
{ 
    foreach (string key in dictionary.Keys) 
    { 
     Console.WriteLine("{0} - {1}", key, dictionary[key]); 
    } 
} 

Einige Details über ein Wörterbuch Initialisierung kann here finden.

7

Ein bisschen wie ein Hack, aber man konnte Message Klasse hat Ihre implementieren Sie die IEnumerable Schnittstelle und geben Sie eine Add Methode. Sie werden dann in der Lage Auflistungsinitialisierer Syntax: gerade erstellt (vor Minuten)

Agent.SendMessage 
(
    new Message(MessageTypes.SomethingHappened) {{ "foo", 42 }, { "bar", 123 }} 
); 

// ... 

public class Message : IEnumerable 
{ 
    private Dictionary<string, object> _map = new Dictionary<string, object>(); 

    public Message(MessageTypes mt) 
    { 
     // ... 
    } 

    public void Add(string key, object value) 
    { 
     _map.Add(key, value); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return ((IEnumerable)_map).GetEnumerator(); 
     // or throw a NotImplementedException if you prefer 
    } 
} 
+0

Dies scheint viel weniger Aufwand als die Reflection-Methode zu haben, und ist wirklich wahrscheinlich kein Hack überhaupt, da ich eine Add-Funktion in auf jeden Fall. – Quantumplation

+1

Je nachdem, wie sehr Ihre "Message" -Klasse sich wie eine echte Sammlung verhalten soll, können Sie stattdessen eine "größere" Schnittstelle als "IEnumerable" implementieren (zum Beispiel "IDictionary "). Ich habe nur "IEnumerable" gewählt, weil es die Syntax der Sammlungsinitialisierung mit minimaler Zusatzarbeit erlaubt! – LukeH

7

Komisch, ich eine Methode, die unter Verwendung von anonymen Typen und Reflexion zu tun erlaubt:

MyMethod(new { Key1 = "value1", Key2 = "value2" }); 


public void MyMethod(object keyValuePairs) 
{ 
    var dic = DictionaryFromAnonymousObject(keyValuePairs); 
    // Do something with the dictionary 
} 

public static IDictionary<string, string> DictionaryFromAnonymousObject(object o) 
{ 
    IDictionary<string, string> dic = new Dictionary<string, string>(); 
    var properties = o.GetType().GetProperties(); 
    foreach (PropertyInfo prop in properties) 
    { 
     dic.Add(prop.Name, prop.GetValue(o, null) as string); 
    } 
    return dic; 
} 
+0

Perfekt! Das ist genau die Art von Lösung, nach der ich gesucht habe. Wie viel Overhead gibt es wegen der Reflektion? – Quantumplation

+0

Keine Ahnung ... Wie auch immer, diese Lösung ist praktisch, aber nicht geeignet, wenn die Leistung für Ihre App entscheidend ist –

1

Sie kann das tun:

TestNamedMethod(DateField => DateTime.Now, IdField => 3); 

wo Datefield und IdField angeblich ein 'string' identif sein iers.

Die TestNameMethod:

public static string TestNameMethod(params Func<object, object>[] args) 
{ 
    var name = (args[0].Method.GetParameters()[0]).Name; 
    var val = args[0].Invoke(null); 
    var name2 = (args[1].Method.GetParameters()[0]).Name; 
    var val2 = args[1].Invoke(null); 
    Console.WriteLine("{0} : {1}, {2} : {3}", name, val, name2, val2); 
} 

Leistung beträgt 5% schneller als Wörterbuch verwenden. Nachteil: Sie können die Variable nicht als Schlüssel verwenden.

2

Mit dynamischem Typ in C# 4.0:

public class MyClass 
{ 
    // Could use another generic type if preferred 
    private readonly Dictionary<string, dynamic> _dictionary = new Dictionary<string, dynamic>(); 

    public void MyFunction(params dynamic[] kvps) 
    { 
     foreach (dynamic kvp in kvps) 
      _dictionary.Add(kvp.Key, kvp.Value); 
    } 
} 

Anruf mit:

MyFunction(new {Key = "Key1", Value = "Value1"}, new {Key = "Key2", Value = "Value2"}); 
0

Sie könnten Tupeln verwenden etwas ähnliches wie @Bryan Watt zu erreichen Pairing.Of ohne Extraklasse:

public static void MyFunction(params Tuple<string, int>[] pairs) 
{ 
} 

MyFunction(Tuple.Create("foo", 1), Tuple.Create("bar", 2)); 
0

Sie könnten auch auf das nuggetpackage "valuetuple" verweisen, was Ihnen erlaubt wie folgt vor:

public static void MyFunction(params ValueTuple<string, object>[] pairs) 
{ 
    var pair = pairs[1]; 
    var stringValue = pair.item1; 
    var objectValue = pair.item2; 
} 

Sie können die Methode wie folgt dann rufen:

MyFunction(("string",object),("string", object)); 
1

Da C# 7.0 können Sie Wertetupeln verwenden. C# 7.0 führt nicht nur einen neuen Typ ein, sondern auch eine neue vereinfachte Syntax für Tupel.

public static void MyFunction(params (string Key, object Value)[] pairs) 
{ 
    foreach (var pair in pairs) { 
     Console.WriteLine($"{pair.Key} = {pair.Value}"); 
    } 
} 

Es ist auch möglich, ein Tupel wie diese

 var (key, value) = pair; 
     Console.WriteLine($"{key} = {value}"); 

Das extrahiert die Elemente des Tupels in zwei getrennten Variablen key und value dekonstruieren.

Jetzt können Sie diese Funktion wie folgt aufrufen mit einer variierenden Anzahl von Argumenten:

MyFunction(("a", 1), ("b", 2), ("c", 3)); 

See: New Features in C# 7.0