views:

2076

answers:

6

I'm looking for a way to have a function such as:

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

I'm sure there's something with anonymous types that would be pretty easy, but I'm not seeing it.

The only solution I can think of is to have a "params KeyValuePair[] pairs" parameter, but that ends up being something similar to:

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

Which is, admittedly, much uglier.

EDIT:

To clarify, I'm writing a "Message" class to pass between 2 different systems. It contains a ushort specifying the the Message Type, and a dictionary of string to object for "Data" associated with the message. I'd like to be able to pass all this information in the constructor, so I am able to do this:

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

+1  A: 

Using a dictionary:

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

Which is straight forward, you need only one new Dictionary<K, V>, not for each argument. It's trivial to get the keys and values.

Or with an anonymous type:

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

Which is not very nice to use inside the function, you'll need reflection. This would look something like this:

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

(Staight from my head, probably some errors...)

Stefan Steinegger
How would I draw the key/value pairs out of the second type?
Quantumplation
You find the second in the ActionLink method in asp.net MVC. Does this need reflection to get the property names and values out?
Chris S
A: 

Use a 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"];
}
JP Alioto
Well yes, but the calling of the function is complicated here by the requirement of declaring a dictionary beforehand. Also, what's the .Dump() for?
Quantumplation
Oops, Dump is from LINQPad, just think Console.Writeline. Why would you contort yourself to have everything in one statement? You should make your interface correct. Worrying about the calling convention should be a distant second.
JP Alioto
It's for the constructor of a Message class, something that's sent quite often, and an unwieldy calling convention is going to get in the way later. having to declare a dictionary, add the keys, and pass it to the constructor for many different instances where messages are sent becomes difficult to tolerate, and distracts a reader/writer away from what's happening, and thus becomes harder to maintain.
Quantumplation
+1  A: 

Here's more of the same:

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

Some details on initialising a dictionary can be found here.

Chris S
+12  A: 

When the syntax is bad for an otherwise decent pattern, change the syntax. How about:

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

Usage:

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

Even more interesting would be to add an extension method to string to make it pairable:

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

Usage:

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

Edit: You can also use the dictionary syntax without the generic brackets by deriving from Dictionary<,>:

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

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

Usage:

MyFunction(new MessageArgs { { "Key1", 5 }, { "Key2", someObject } });
Bryan Watts
Hmm, this is a clever solution, I like it. I think this is what i'll use. Thank you. I'm going to leave the question unanswered for an hour or two to see if any other solutions come up as well.
Quantumplation
+4  A: 

A bit of a hack, but you could have your Message class implement the IEnumerable interface and give it an Add method. You'll then be able to use collection initializer syntax:

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
    }
}
LukeH
This seems to have much less overhead than the Reflection method, and is really probably not a hack at all, since I want an add function in any case.
Quantumplation
Depending on how much you want your `Message` class to behave like a real collection, you could implement a "bigger" interface than `IEnumerable` instead (for example, `IDictionary<K,V>`). I only chose `IEnumerable` because it allows collection initializer syntax with the minimum amount of extra work!
LukeH
+2  A: 

Funny, I just created (minutes ago) a method that allows to do that, using anonymous types and reflection :

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;
}
Thomas Levesque
Perfect! This is just the kind of solution I was looking for. How much overhead is there because of the reflection?
Quantumplation
No idea... Anyway, this solution is handy, but not suitable if performance is critical for your app
Thomas Levesque