2009-08-23 15 views
24

Szukam sposobu, aby mieć funkcję takie jak:pary wartość klucza w C# Params

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

Jestem pewien, że coś z anonimowych typów, które byłyby całkiem proste, ale nie jestem widząc to.

Jedynym rozwiązaniem można myślę, jest mieć parametr „params KeyValuePair [] par”, ale kończy się coś podobnego do:

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

Która jest, co prawda, o wiele brzydsze.

EDIT:

celu wyjaśnienia, Piszę "message" klasy do przejść między 2 różnych systemów. Zawiera ushort określający typ komunikatu i słownik ciągu znaków do obiektu dla "danych" związanych z wiadomością. Chciałbym móc przekazać wszystkie te informacje w konstruktorze, więc jestem w stanie to zrobić:

Agent.SendMessage (nowa wiadomość (MessageTypes.SomethingHappened, "A", x, "B", y , "C", z)); lub podobna składnia.

+0

I edytowane moją odpowiedź na to rozsądny sposób na wykorzystanie składni słownika. Pamiętaj, aby to sprawdzić. –

Odpowiedz

52

Gdy składnia jest zła w przypadku przyzwoitego wzoru, zmień składnię.Jak o:

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

Zastosowanie:

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

Jeszcze bardziej interesujące byłoby dodać metodę rozszerzenia do string aby pairable:

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

Zastosowanie:

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

Edit: Można również użyć składni słownika bez nawiasów generycznych poprzez wyprowadzenie z Dictionary<,>:

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

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

Zastosowanie:

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

Hmm, to sprytne rozwiązanie, podoba mi się. Myślę, że właśnie tego użyję. Dziękuję Ci. Zamierzam pozostawić pytanie bez odpowiedzi przez godzinę lub dwie, aby sprawdzić, czy pojawią się jakieś inne rozwiązania. – Quantumplation

+0

Możesz też pójść boksem i zrobić 'MyFunction (parametry params obiektu [])' –

2

Korzystanie ze słownika:

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

Która jest prosta, trzeba tylko jeden new Dictionary<K, V>, nie dla każdego argumentu. Zdobycie kluczy i wartości jest banalne.

lub z rodzaju anonimowego:

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

co nie jest bardzo miły w użyciu wewnątrz funkcji, trzeba refleksji. To będzie wyglądać mniej więcej tak:

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

(staight z mojej głowy, prawdopodobnie pewnymi błędami ...)

+0

Jak mogę narysować pary klucz/wartość z drugiego typu? – Quantumplation

+0

Znajdziesz drugą w metodzie ActionLink w asp.net MVC. Czy to wymaga refleksji, aby uzyskać nazwy i wartości właściwości? –

2

Użyj 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

No tak, ale wywołanie funkcji jest tu skomplikowane przez wymóg wcześniejszego deklarowania słownika. Co to jest .Dump() dla? – Quantumplation

+0

Ups, Dump jest z LINQPad, po prostu myśl Console.Writeline. Dlaczego miałbyś się zmusić do posiadania wszystkiego w jednym oświadczeniu? Powinieneś poprawić swój interfejs. Martwienie się konwencją wzywającą powinno być odległym drugim. –

+2

Jest to dla konstruktora klasy wiadomości, coś, co jest wysyłane dość często, a nieporęczna konwencja wywoływania wejdzie w drogę później. konieczność zadeklarowania słownika, dodania kluczy i przekazania go konstruktorowi dla wielu różnych przypadków, w których wysyłane są wiadomości, staje się trudny do zniesienia i odwraca uwagę czytelnika od tego, co się dzieje, a tym samym staje się trudniejszy w utrzymaniu. – Quantumplation

2

Oto więcej tego samego:

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

Niektóre szczegóły dotyczące inicjowania słownika można znaleźć pod numerem here.

7

Trochę hack, ale można mieć swoją klasę Message zaimplementuj interfejs IEnumerable i przekaż mu metodę Add. Będziesz wtedy mógł używać kolekcja initializer składnię:

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

Wydaje się, że ma to znacznie mniejszy wpływ na wyniki niż metoda Reflection i naprawdę nie jest to wcale hack, ponieważ chcę dodać funkcję w każdym przypadku. – Quantumplation

+1

W zależności od tego, jak bardzo chcesz, aby twoja klasa "Wiadomość" zachowywała się jak prawdziwa kolekcja, możesz zaimplementować "większy" interfejs niż "IEnumerable" (na przykład 'IDictionary '). Wybrałem tylko 'IEnumerable', ponieważ pozwala on na składowanie inicjalizatora kolekcji przy minimalnej ilości dodatkowej pracy! – LukeH

7

śmieszne, po prostu stworzone (minuty temu) metodę, która pozwala to zrobić, stosując anonimowe typy i odbicie:

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

Idealny! Jest to po prostu rodzaj rozwiązania, którego szukałem. Ile kosztu jest tam z powodu refleksji? – Quantumplation

+0

Nie mam pojęcia ... W każdym razie to rozwiązanie jest przydatne, ale nie nadaje się, jeśli wydajność ma krytyczne znaczenie dla Twojej aplikacji –

1

You można to zrobić:

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

gdzie DateField i IdField mają być 'string' Ident ery.

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

Wydajność wynosi 5% szybciej niż przy użyciu Słownik. Wada: nie można używać zmiennej jako klucza.

2

Z dynamicznym typu w 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); 
    } 
} 

Połącz używając:

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

Można użyć krotek, aby osiągnąć coś podobnego do @Bryan Watts Pairing.Of bez dodatkowej klasy:

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

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

Można również odwołać się do pakietu nugetpackage "valuetuple", który pozwala wykonaj następujące czynności:

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

Następnie można wywołać metodę tak:

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

Ponieważ C# 7.0, można użyć krotki wartości. C# 7.0 nie tylko wprowadza nowy typ, ale także nową uproszczoną składnię dla krotek.

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

Możliwe jest również do dekonstrukcji krotki jak ten

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

ten wydobywa elementy krotki w dwóch oddzielnych zmiennych key i value.

Teraz można wywołać tę funkcję jak ten z różną liczbą argumentów:

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

Patrz: New Features in C# 7.0

Powiązane problemy