2009-08-23 3 views
24

Я ищу способ иметь такую ​​функцию, как:пар ключ-значение в C# Params

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

Я уверен, что есть что-то с анонимными типами, было бы довольно легко, но я не посмотрев.

Единственное решение, которое я могу думать о том, чтобы иметь параметр «PARAMS KeyValuePair [] пар», но заканчивает тем, что что-то подобное:

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

который, по общему признанию, гораздо уродливее.

EDIT:

Чтобы уточнить, я пишу класс "Message", чтобы пройти между 2 различными системами. Он содержит пользовательский указатель, определяющий тип сообщения, и словарь строки для объекта для «Данные», связанного с сообщением. Я хотел бы иметь возможность передать всю эту информацию в конструкторе, поэтому я могу это сделать:

Agent.SendMessage (новое сообщение (MessageTypes.SomethingHappened, "A", x, "B", y , "C", z)); или аналогичный синтаксис.

+0

Я отредактировал мой ответ включать разумный способ использования словаря синтаксиса. Обязательно проверьте это. –

ответ

52

Если синтаксис плохо подходит для подходящего шаблона, измените синтаксис.Как насчет:

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

Использование:

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

Еще более интересно было бы добавить метод расширения для string, чтобы сделать его спариваемая:

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

Использование:

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

Edit: Вы можете также использовать словарь синтаксис без общих скобок, выводя из Dictionary<,>:

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

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

Использование:

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

Хм, это умное решение, мне оно нравится. Я думаю, что это то, что я буду использовать. Спасибо. Я собираюсь оставить вопрос без ответа в течение часа или двух, чтобы узнать, появляются ли какие-либо другие решения. – Quantumplation

+0

Или вы можете пойти в боксерский маршрут и сделать 'MyFunction (params object [] args)' –

2

Использование словаря:

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

Который прямо вперед, вам нужно всего лишь один new Dictionary<K, V>, а не для каждого аргумента. Тривиально получить ключи и ценности.

Или с анонимного типа:

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

Что не очень хорошо использовать внутри функции, вам необходимо отражение. Это будет выглядеть примерно так:

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

(Staight из моей головы, вероятно, некоторые ошибки ...)

+0

Как бы нарисовать пары ключ/значение второго типа? – Quantumplation

+0

Вы находите второе в методе ActionLink в asp.net MVC. Нужно ли это размышлять, чтобы получить имена и ценности свойств? –

2

Используйте 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

Ну да, но вызов функции здесь затруднен требованием объявления словаря заранее. Кроме того, что такое .Dump()? – Quantumplation

+0

Упс, Дамп от LINQPad, просто подумайте о Console.Writeline. Зачем вам судить о том, чтобы все было в одном заявлении? Вы должны сделать свой интерфейс правильным. Беспокойство по поводу вызывающей конвенции должно быть отдаленной секундой. –

+2

Это для конструктора класса Message, что-то, что отправлено довольно часто, и громоздкое соглашение о вызове будет уложено позже. нужно объявить словарь, добавить ключи и передать его конструктору для разных случаев, когда сообщения отправляются, становится трудно переносить и отвлекает читателя/писателя от того, что происходит, и, следовательно, становится сложнее поддерживать. – Quantumplation

2

Вот больше того же самого:

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

Некоторые подробности по инициализации словаря можно найти here.

7

Немного взломать, но вы могли бы иметь свой Message класс реализовать интерфейс IEnumerable и дать ему метод Add. После этого вы сможете использовать коллекции инициализатора синтаксис:

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

У него, кажется, гораздо меньше накладных расходов, чем метод Reflection, и на самом деле это, вероятно, вовсе не хак, так как я хочу добавить функцию в любой случай. – Quantumplation

+1

В зависимости от того, насколько вы хотите, чтобы ваш класс 'Message' вел себя как реальная коллекция, вы могли бы реализовать« больший »интерфейс, чем' IEnumerable' вместо (например, 'IDictionary '). Я выбрал только «IEnumerable», потому что он позволяет синтаксис инициализатора коллекции с минимальным количеством дополнительной работы! – LukeH

7

Забавно, я только что создал (минуты назад) метод, который позволяет сделать это, используя анонимные типы и отражение:

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

Отлично! Это именно то решение, которое я искал. Сколько накладных расходов происходит из-за отражения? – Quantumplation

+0

Не знаю ... Во всяком случае, это решение удобно, но не подходит, если производительность важна для вашего приложения. –

1

You можно сделать:

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

где DateField и IdField должны быть «строка» IDENTIF IERS.

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

Производительность 5% быстрее, чем при использовании словаря. Недостаток: вы не можете использовать переменную в качестве ключа.

2

С динамического типа в 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); 
    } 
} 

Вызов с помощью:

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

Вы можете использовать кортежей, чтобы достичь что-то похожее на @Bryan Уоттса Pairing.Of без дополнительного класса:

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

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

Вы также можете обратиться к nugetpackage «valuetuple», который позволяет вам выполните следующие действия:

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

Вы можете вызвать метод как это:

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

Поскольку C# 7.0, вы можете использовать значение кортежи. C# 7.0 не только вводит новый тип, но и новый упрощенный синтаксис для кортежей.

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

Также можно разобрать кортеж, как этот

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

Это извлекает элементы кортежа в двух отдельных переменных key и value.

Теперь вы можете вызвать эту функцию, как это с переменным числом аргументов:

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

См: New Features in C# 7.0