Пары ключ-значение в C # Params - PullRequest
28 голосов
/ 24 августа 2009

Я ищу способ иметь такую ​​функцию, как:

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

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

Единственное решение, которое я могу придумать, - это иметь параметр "params KeyValuePair [] пары", но в итоге получается что-то похожее на:

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

Что, правда, намного страшнее.

EDIT:

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

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

Ответы [ 11 ]

60 голосов
/ 24 августа 2009

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

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

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

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

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

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

MyFunction(new MessageArgs { { "Key1", 5 }, { "Key2", someObject } });
9 голосов
/ 24 августа 2009

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

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;
}
7 голосов
/ 08 декабря 2017

Начиная с 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 с различным количеством аргументов:

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

Это позволяет нам делать такие вещи, как

DrawLine((0, 0), (10, 0), (10, 10), (0, 10), (0, 0));

См .: Новые функции в C # 7.0

7 голосов
/ 24 августа 2009

Немного хак, но вы могли бы сделать так, чтобы ваш класс 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
    }
}
2 голосов
/ 13 мая 2016

С динамическим типом в 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"});
2 голосов
/ 24 августа 2009

Вот еще то же самое:

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

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

2 голосов
/ 24 августа 2009

Используйте словарь ...

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"];
}
2 голосов
/ 24 августа 2009

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

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

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

1 голос
/ 25 декабря 2012

Вы можете сделать это:

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

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

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% выше, чем при использовании словаря. Недостаток: вы не можете использовать переменную в качестве ключа.

0 голосов
/ 08 декабря 2017

Вы также можете сослаться на пакет nuget "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));
...