【问题标题】:Key value pairs in C# ParamsC# Params 中的键值对
【发布时间】:2010-11-22 03:01:59
【问题描述】:

我正在寻找一种具有以下功能的方法:

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

我确信匿名类型的某些东西会很容易,但我没有看到它。

我能想到的唯一解决方案是有一个params KeyValuePair<String, object>[] pairs 参数,但最终类似于:

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

诚然,这更丑陋。

编辑:

为了澄清,我正在编写一个Message 类来在两个不同的系统之间传递。它包含指定消息类型的ushort,以及与消息关联的“数据”对象的字符串字典。我希望能够在构造函数中传递所有这些信息,所以我可以这样做:

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

或类似的语法。

【问题讨论】:

  • 我编辑了我的答案以包含使用字典语法的合理方式。请务必检查一下。

标签: c# parameters key-value


【解决方案1】:

使用字典:

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

这很简单,您只需要一个new Dictionary&lt;K, V&gt;,而不是每个参数。获取键和值很简单。

或者使用匿名类型:

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

在函数内部使用不是很好,您需要反射。这看起来像这样:

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

(直接从我的脑海中,可能有些错误......)

【讨论】:

  • 如何从第二种类型中提取键/值对?
  • 你可以在 asp.net MVC 的 ActionLink 方法中找到第二个。这需要反射来获取属性名称和值吗?
【解决方案2】:

使用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"];
}

【讨论】:

  • 嗯,是的,但是这里的函数调用很复杂,因为需要事先声明一个字典。另外,.Dump() 有什么用?
  • 糟糕,Dump 来自 LINQPad,想想 Console.Writeline。为什么你会扭曲自己以在一个声明中包含所有内容?你应该让你的界面正确。担心调用约定应该是遥远的第二个。
  • 它是用于 Message 类的构造函数的,它经常发送,并且稍后会遇到笨拙的调用约定。对于发送消息的许多不同实例,必须声明一个字典、添加键并将其传递给构造函数变得难以容忍,并且会分散读者/作者对正在发生的事情的注意力,因此变得更难维护。
【解决方案3】:

这里有更多相同的:

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

关于初始化字典的一些细节可以在here找到。

【讨论】:

    【解决方案4】:

    当语法对其他体面的模式不利时,请更改语法。怎么样:

    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&lt;,&gt; 派生来使用不带通用括号的字典语法:

    public void MyFunction(MessageArgs args)
    {
        // ...
    }
    
    public class MessageArgs : Dictionary<string, object>
    {}
    

    用法:

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

    【讨论】:

    • 嗯,这是一个聪明的解决方案,我喜欢它。我想这就是我要使用的。谢谢你。我将把这个问题搁置一两个小时,看看是否还有其他解决方案。
    • 或者你可以去拳击路线做MyFunction(params object[] args)
    • 我来自 C++11,因此我喜欢你的最后一个建议。这让我看起来更 native 并且我发现它也更具可读性。
    【解决方案5】:

    有点小技巧,但是你可以让你的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
        }
    }
    

    【讨论】:

    • 这似乎比 Reflection 方法的开销要少得多,而且真的可能根本不是 hack,因为无论如何我都想要一个 add 函数。
    • 根据您希望Message 类表现得像真正的集合的程度,您可以实现比IEnumerable 更大的接口(例如IDictionary&lt;K,V&gt;)。我只选择了IEnumerable,因为它允许集合初始化器语法以最少的额外工作量!
    【解决方案6】:

    有趣的是,我刚刚(几分钟前)创建了一个允许这样做的方法,使用匿名类型和反射:

    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】:

    你可以这样做:

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

    其中 DateFieldIdField 应该是“字符串”标识符。

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

    性能比使用 Dictionary 快 5%。缺点:不能使用变量作为键。

    【讨论】:

      【解决方案8】:

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

      【讨论】:

        【解决方案9】:

        您可以使用元组来实现类似于@Bryan Watts 的Pairing.Of 而无需额外的类:

        public static void MyFunction(params Tuple<string, int>[] pairs)
        {
        }
        
        MyFunction(Tuple.Create("foo", 1), Tuple.Create("bar", 2));
        

        【讨论】:

          【解决方案10】:

          您还可以引用 nugetpackage“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));
          

          【讨论】:

            【解决方案11】:

            从 C# 7.0 开始,您可以使用值元组。 C# 7.0 不仅引入了一种新类型,而且为元组类型和元组值引入了简化的语法。元组类型可以简单地写成用大括号括起来的类型列表:

            (string, int, double)
            

            对应的元素命名为Item1Item2Item2。您还可以指定可选别名。这些别名只是语法糖(C# 编译器的一个技巧);元组仍然基于不变(但通用)System.ValueTuple&lt;T1, T2, ...&gt; struct

            (string name, int count, double magnitude)
            

            元组值具有类似的语法,不同之处在于您指定表达式而不是类型

            ("test", 7, x + 5.91)
            

            或使用别名

            (name: "test", count: 7, magnitude: x + 5.91)
            

            params 数组示例:

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

            这会在两个单独的变量keyvalue 中提取元组的项目。

            现在,您可以使用不同数量的参数轻松调用 MyFunction

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

            它允许我们做类似的事情

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

            见:New Features in C# 7.0

            【讨论】:

            • 对我来说,这是最优雅的解决方案。
            【解决方案12】:

            所以我是新手,目前无法添加 cmets,但这只是一个建议,以改进 @Bryan Watts 对 Pairing.of 类的想法,使其具有通用性,使其易于被其他类使用。

            public class Pairing 
            {
                public static KeyValuePair<TKey, TValue> of<TKey, TValue>(TKey key, TValue value)
                {
                    return new KeyValuePair<TKey, TValue>(key, value);
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-08-17
              • 1970-01-01
              • 2013-04-18
              • 1970-01-01
              • 1970-01-01
              • 2014-12-14
              • 1970-01-01
              相关资源
              最近更新 更多