可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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:
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 } });
回答2:
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;
}
回答3:
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
}
}
回答4:
Since C# 7.0, you can use value tuples. C# 7.0 not only introduces a new type but a simplified syntax for tuple types as well as for tuple values.
public static void MyFunction(params (string Key, object Value)[] pairs)
{
foreach (var pair in pairs) {
Console.WriteLine($"{pair.Key} = {pair.Value}");
}
}
It is also possible to deconstruct a tuple like this
var (key, value) = pair;
Console.WriteLine($"{key} = {value}");
This extracts the items of the tuple in two separate variables key
and value
.
Now, you can call MyFunction
with a varying number of arguments easily:
MyFunction(("a", 1), ("b", 2), ("c", 3));
See: New Features in C# 7.0
回答5:
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"];
}
回答6:
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.
回答7:
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...)
回答8:
With dynamic type in 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);
}
}
Call using:
MyFunction(new {Key = "Key1", Value = "Value1"}, new {Key = "Key2", Value = "Value2"});
回答9:
You can do that:
TestNamedMethod(DateField => DateTime.Now, IdField => 3);
where DateField and IdField are supposed to be a 'string' identifiers.
The 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);
}
Performance is 5% faster than using Dictionary. Disadvantage: you can't use variable as a key.
回答10:
You could use Tuples to achieve something similar to @Bryan Watts's Pairing.Of
without the extra class:
public static void MyFunction(params Tuple<string, int>[] pairs)
{
}
MyFunction(Tuple.Create("foo", 1), Tuple.Create("bar", 2));
回答11:
You could also reference the nugetpackage "valuetuple", which allows you to do the following:
public static void MyFunction(params ValueTuple<string, object>[] pairs)
{
var pair = pairs[1];
var stringValue = pair.item1;
var objectValue = pair.item2;
}
You can then call the method like this:
MyFunction(("string",object),("string", object));