Pares de valores clave en parámetros de C#


Estoy buscando una manera de tener una función como:

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

Estoy seguro de que hay algo con tipos anónimos que sería bastante fácil, pero no lo estoy viendo.

La única solución que se me ocurre es tener un parámetro "params KeyValuePair[] pairs", pero que termina siendo algo similar a:

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

Que es, ciertamente, mucho más feo.

EDITAR:

Para aclarar, estoy escribiendo una clase "Message" para pasar entre 2 sistemas diferentes. Contiene un ushort especificando el Tipo de Mensaje, y un diccionario de string to object para" Data " asociado con el mensaje. Me gustaría poder pasar toda esta información en el constructor, así que soy capaz de hacer esto:

Agente.SendMessage (new Message (MessageTypes.SomethingHappened, "A", x," B", y," C", z)); o sintaxis similar.

Author: Quantumplation, 2009-08-24

11 answers

Cuando la sintaxis es mala para un patrón decente, cambie la sintaxis. Qué tal:

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

Uso:

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

Aún más interesante sería agregar un método de extensión a string para hacerlo pairable:

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

Uso:

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

Editar : También puede usar la sintaxis del diccionario sin los corchetes genéricos derivando de Dictionary<,>:

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

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

Uso:

MyFunction(new MessageArgs { { "Key1", 5 }, { "Key2", someObject } });
 56
Author: Bryan Watts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-23 22:51:59

Gracioso, acabo de crear (hace minutos) un método que permite hacer eso, usando tipos anónimos y reflexión:

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;
}
 8
Author: Thomas Levesque,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-24 00:45:29

Un poco de un truco, pero usted podría tener su clase Message implementar la interfaz IEnumerable y darle un método Add. A continuación, podrá utilizar la sintaxis del inicializador de colecciones:

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
    }
}
 7
Author: LukeH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-24 00:12:37

Use un Diccionario ...

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
Author: JP Alioto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-23 22:22:56

Aquí hay más de lo mismo:

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

Algunos detalles sobre la inicialización de un diccionario se pueden encontrar aquí.

 2
Author: Chris S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-23 22:27:53

Usando un diccionario:

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

Que es sencillo, solo necesita uno new Dictionary<K, V>, no para cada argumento. Es trivial obtener las claves y los valores.

O con un tipo anónimo:

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

Que no es muy agradable de usar dentro de la función, necesitará reflexión. Esto se vería algo como esto:

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

(Desde mi cabeza, probablemente algunos errores...)

 2
Author: Stefan Steinegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-23 22:30:11

Con tipo dinámico en 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);
    }
}

Llamada usando:

MyFunction(new {Key = "Key1", Value = "Value1"}, new {Key = "Key2", Value = "Value2"});
 2
Author: user3074376,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-13 08:39:06

Desde C# 7.0, puede usar tuplas de valor. C # 7.0 no solo introduce un nuevo tipo, sino una nueva sintaxis simplificada para las tuplas.

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

También es posible deconstruir una tupla como esta

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

Esto extrae los elementos de la tupla en dos variables separadas key y value.

Ahora, puedes llamar a MyFunction con un número variable de argumentos fácilmente:

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

Ver: Nuevas características en C# 7.0

 2
Author: Olivier Jacot-Descombes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-02 13:58:39

Puedes hacer eso:

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

Donde DateField y IdField se supone que son identificadores 'string'.

El método Testnam :

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

El rendimiento es un 5% más rápido que el uso del diccionario. Desventaja: no se puede usar variable como clave.

 1
Author: yeroo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-25 10:08:25

Podrías usar Tuplas para lograr algo similar a @Bryan Watts Pairing.Of sin la clase extra:

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

MyFunction(Tuple.Create("foo", 1), Tuple.Create("bar", 2));
 0
Author: Pakman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-19 17:09:25

También puede hacer referencia al nugetpackage "valuetuple", que le permite hacer lo siguiente:

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

Luego puedes llamar al método de esta manera:

MyFunction(("string",object),("string", object));
 0
Author: Rob. E,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-08 13:25:51