2009-08-23 13 views
24

estoy buscando una manera de tener una función como:pares de valores clave en C# Parámetros

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

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

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

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

Cuál es, sin duda, mucho más feo.

EDIT:

Para aclarar, estoy escribiendo una clase "mensaje" para pasar entre 2 sistemas diferentes. Contiene un ushort especificando el tipo de mensaje, y un diccionario de cadena a objeto para "Datos" asociados con el mensaje. Me gustaría poder pasar toda esta información en el constructor, así que puedo hacer esto:

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

+0

he editado mi respuesta para incluir una manera razonable utilizar la sintaxis de diccionario. Asegúrate de revisarlo. –

Respuesta

52

Cuando la sintaxis es mala para un patrón por lo demás 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 añadir un método de extensión a string para que sea emparejable:

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 utilizar la sintaxis diccionario sin los soportes genéricos mediante la derivación de Dictionary<,>:

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

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

Uso:

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

Hmm, esta es una solución inteligente, me gusta. Creo que esto es lo que usaré. Gracias. Voy a dejar la pregunta sin respuesta durante una hora o dos para ver si también surgen otras soluciones. – Quantumplation

+0

O puede ir por la ruta del boxeo y hacer 'MyFunction (params object [] args)' –

2

El uso de un diccionario:

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

Cuál es sencillo, sólo se necesita un new Dictionary<K, V>, no para cada argumento. Es trivial obtener las llaves 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á la reflexión. Esto sería algo como esto:

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

(Staight de la cabeza, probablemente algunos errores ...)

+0

¿Cómo sacaría los pares clave/valor del segundo tipo? – Quantumplation

+0

Encuentra el segundo en el método ActionLink en asp.net MVC. ¿Esto necesita reflexión para sacar los nombres y valores de las propiedades? –

2

Utilice un 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

Bueno, sí, pero el llamado de la función es complicado aquí por el requisito de declarar un diccionario de antemano. Además, ¿para qué es el .Dump()? – Quantumplation

+0

Vaya, Dump es de LINQPad, solo piensa en Console.Writeline. ¿Por qué te contorsionarías para tener todo en una sola declaración? Debes hacer que tu interfaz sea correcta. Preocuparse por la convención de llamadas debería ser un segundo distante. –

+2

Es para el constructor de una clase de mensaje, algo que se envía con bastante frecuencia, y una convención de llamadas difícil de manejar se interpondrá en el camino más adelante. tener que declarar un diccionario, agregar las claves y pasarlo al constructor para muchas instancias diferentes donde se envían los mensajes se vuelve difícil de tolerar, y distrae a un lector/escritor de lo que está sucediendo, y por lo tanto se vuelve más difícil de mantener. – Quantumplation

2

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

Se pueden encontrar algunos detalles sobre la inicialización de un diccionario here.

7

Un poco de un truco, pero que podría tener su clase Message implementar la interfaz IEnumerable y darle un método Add. A continuación, ser capaz de utilizar la sintaxis de recogida de inicialización:

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

Esto parece tener mucho menos sobrecarga que el método Reflection, y realmente no es un hack en absoluto, ya que quiero una función add en cualquier caso. – Quantumplation

+1

Dependiendo de cuánto desee que su clase 'Message' se comporte como una colección real, podría implementar una interfaz" más grande "que' IEnumerable' en su lugar (por ejemplo, 'IDictionary '). ¡Solo elegí 'IEnumerable' porque permite la sintaxis del inicializador de colecciones con la cantidad mínima de trabajo extra! – LukeH

7

divertido, me acaba de crear (hace minutos) un método que permite hacer eso, el uso de 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; 
} 
+0

¡Perfecto! Este es el tipo de solución que estaba buscando. ¿Cuánta sobrecarga hay debido a la reflexión? – Quantumplation

+0

No tengo idea ... De todos modos, esta solución es útil, pero no adecuada si el rendimiento es crítico para su aplicación –

1

usted puede hacer eso:

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

donde DateField y campo ID se supone que es un identifi 'cadena' iers.

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

rendimiento es 5% más rápido que en un diccionario. Desventaja: no se puede usar la variable como clave.

2

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

Se puede usar tuplas para lograr algo similar a @Bryan vatios de 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

También podría hacer referencia al nugetpackage "valuetuple", que le permite haga lo siguiente:

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

continuación, se puede llamar al método como este:

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

Puesto que C# 7.0, puede utilizar tuplas de valor. C# 7.0 no solo presenta un nuevo tipo sino una nueva sintaxis simplificada para 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 para deconstruir una tupla como este

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

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

Ahora se puede llamar a esta función como esta con un número variable de argumentos:

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

Ver: New Features in C# 7.0

Cuestiones relacionadas