【问题标题】:Is there a more efficient way than using dictionary as parameter有没有比使用字典作为参数更有效的方法
【发布时间】:2017-12-09 21:20:26
【问题描述】:

有没有更有效的方法来做到这一点。

功能:

public void someFunction(Dictionary<string, object> args){
   foreach (var item in args){
       Console.WriteLine("key: " + item.Key + " value: " + item.Value);
   }
}

调用看起来不太好的函数:

someFunction(new Dictionary<string, object>
        {
            { "number", 3 },
            { "text", "Some Text" }
}));

输出:

key: number value: 3
key: text value: Some Text

我想实现这样的目标我可以随时更改传递的变量名称和值

someFunction(["number" => 3, "text" => "Some Text"]);

我想将一个字符串名称和一个对象类型数组传递给函数,以便可以遍历它。此代码有效,但我希望它看起来更整洁。

在 PHP 中有http://php.net/manual/en/function.compact.php

这使得它能够在您的代码中使用变量的名称和数组中的值。使用 c# 我必须使用整个字典。

我想看看更有效的方法。

编辑: 谢谢你们。不知道我会这么快得到答案0_0

【问题讨论】:

  • 您可以使用多值字典。例如Dictionary&lt;Key, List&lt;Values&gt;&gt; 或类,在您的情况下,您只会迭代List&lt;object&gt;
  • 我不认为你可以直接迭代字典,但你可以迭代它的 Keys 集合。你的字典有性能问题吗?
  • 你可以使用一个类...
  • 如果你只是需要一些东西来遍历你不应该使用字典来将字符串与某个对象结合起来。这可以通过插入不那么慢的结构列表来完成。
  • 我的问题是调用函数。它需要更多的行,看起来不太好,因为在 php 中我可以在一行中完成所有操作。

标签: c#


【解决方案1】:

如果您只是为调用者寻找稀疏语法,您可以使用匿名类型,类似于 jquery 传递可选选项的方式。示例:

public static void someFunction(object input)
{
    foreach (var p in input.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
             Console.WriteLine("{0}={1}", p.Name, p.GetValue(input));
}

函数本身有点杂乱,但对于调用者来说,语法相当轻量:

someFunction( new { Number = 3, Text = "Some text"});

输出:

Number=3
Text=Some text

如果您打算经常这样做,您可以使用扩展方法减轻痛苦。我在下面命名为Extract()

using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;

public static class ExtensionMethods
{
    public static Dictionary<string, object> Extract<T>(this T input) where T : class
    {
        return input
            .GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .ToDictionary( p => p.Name, p => p.GetValue(input));
    }
}

public class Program
{
    public static void someFunction(object input)
    {
        var parameters = input.Extract();  //Magic

        Console.WriteLine("There were {0} parameters, as follows:", parameters.Count);
        foreach (var p in parameters)
        {
            Console.WriteLine("{0}={1}", p.Key, p.Value);
        }
    }

    public static void Main()
    {
        someFunction(new { number = 3, text = "SomeText" } );
        someFunction(new { another = 3.14F, example = new DateTime(2017, 12, 15) } );
    }
}

DotNetFiddle 上的输出:

There were 2 parameters, as follows:
number=3
text=SomeText
There were 2 parameters, as follows:
another=3.14
example=12/15/2017 12:00:00 AM

这种方法的主要缺点是您不能使用接口或执行任何要求某些键存在的操作。此外,您必须使用反射,这需要更多的工作。如果您想避免这些缺点,您可以使用相同的方法,但使用非匿名类型,如下所示:

class SomeFunctionParams
{
    public int Number { get; set; }
    public string Text { get; set; }
}

public static void someFunction(SomeFunctionParams params)
{
    Console.WriteLine("Number={0}", params.Number);
    Console.WriteLine("Text={0}", params.Text);
}

...但是你会失去匿名类型的动态优势。

另一种选择是将所有可能的键作为可选参数公开,并且只提供你想要的:

void someFunction(int Number = default(int), string Text = default(string), DateTime SomeOtherParam = default(DateTime))
{
    if (Number != default(int)) Console.WriteLine("Number={0}", Number);
    if (Text != default(string)) Console.WriteLine("Text={0}", Text);
    if (SomeOtherParam != default(DateTime)) Console.WriteLine("SomeOtherParam={0}", SomeOtherParam);
}

someFunction(Number : 3, Text : "Some text");

【讨论】:

  • 就个人而言,我喜欢它,因为它还确保您不会发送相同的密钥,并且它看起来最像 OP 想要的实现。但是,我个人希望了解有关此方法的缺点的更多信息
  • 你能不能说一下如何从对象类型中选择count()?同样使用第二个选项,您将无法更改变量名称。
  • 这个解决方法是传奇。似乎如果我想提出它,我需要更多的 c# 知识,然后我现在就拥有了。 (反射等)因此,为什么我问怎么做 count()
【解决方案2】:

当然,您可以使用 params 关键字来允许动态数量的参数

然后你可以做(​​不一定是KeyValuePair&lt;string, object&gt;,也可以是Tuple&lt;string, object&gt;

public void AddSomeKeys(params KeyValuePair<string, object>[] arguments) {
    foreach (var item in arguments){
        Console.WriteLine($"key: {item.Key} value: {item.Value}");
    }
}

在 C# 7 中,您可以使用 ValueTuple 使其更容易一些

public void AddSomeKeys(params (string key, object value)[] arguments) {
    foreach (var item in arguments){
        Console.WriteLine($"key: {item.key} value: {item.value}");
    }
}

或者你可以使用更匿名的 ValueTuple

public void AddSomeKeys(params (string, object)[] arguments) {
    foreach (var item in arguments){
        Console.WriteLine($"key: {item.Item1} value: {item.Item2}");
    }
}

在这两种情况下,您都可以调用如下方法:

AddSomeKeys(new KeyValuePair<string, object>( "item1", "value1" ), new KeyValuePair<string, object>( "item2", "value2" ) );

或者用第二个版本

AddSomeKeys( ( key: "item1", value: "value1" ), ( key: "item2", value: "value2" ) );

或者第三个版本

AddSomeKeys( ( "item1", "value1" ), ( "item2", "value2" ) );

【讨论】:

  • @ZoharPeled 我确实提到了:)
  • 正确,没看到,抱歉。
  • 有一个包可以使用 ValueTuple stackoverflow.com/a/40826779/5517161
  • 这个函数很好,但我不能传递整数,只能传递字符串。为其他读者更改带有对象的第二个字符串。
  • @MoDGenesis 是的,我将其更新为使用字符串、对象(并添加了字符串插值而不是连接)
【解决方案3】:

C# 6 added a new syntax 使内联初始化更易于阅读:

someFunction(new Dictionary<string, object> {
    ["number"]=3, ["text"]="Some Text"
});

【讨论】:

    【解决方案4】:

    怎么样:

    public static void Main()
    {
        Iterate(new Dictionary<string, object> { ["1"] = "one", ["2"] = "Two" });
        Console.Read();
    }
    
    public static void Iterate(Dictionary<string, object> args)
    {
        foreach (var item in args)
        {
            Console.WriteLine("key: " + item.Key + " value: " + item.Value);
        }
    }
    

    【讨论】:

      【解决方案5】:

      您可以在文件开头使用alias

      using D = System.Collections.Generic.Dictionary<string, object>;
      

      using System.Collections.Generic;
      
      Namespace {
          using D = Dictionary<string, object>;
      

      然后使用D 而不是Dictionary&lt;string, object&gt;

      public void someMethod(D args){
         foreach (var item in args){
             Console.WriteLine("key: " + item.Key + " value: " + item.Value);
         }
      }
      

      someMethod(new D { { "number", 3 }, { "text", "Some Text" } }));
      

      如果所有参数都已知,使用optional arguments 会更有效:

      void someMethod(int a = 0, int b = 0, int c = 0, int d = 0, int e = 0){
          Console.WriteLine($"key: {nameof(a)} value: {a}");
          Console.WriteLine($"key: {nameof(b)} value: {b}");
          // ...
      }
      

      和样品使用:

      someMethod(d: 1, b: 2);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-09-30
        • 1970-01-01
        • 2021-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多