【问题标题】:When should I use params Object[] versus Dictionary<String, Object>?什么时候应该使用 params Object[] 与 Dictionary<String, Object>?
【发布时间】:2012-07-08 04:55:30
【问题描述】:

我将 API 定义为一个接口,我们将调用它 IFoo 并且我想定义一个方法 Bar()

此方法Bar() 将采用一个必需的参数,然后是任意数量的其他参数。这些其他参数的解释将取决于IFoo的实现者

对于这种情况,使用params 或使用Dictionary&lt;String, Object&gt; 来定义我的界面是否更合适,例如

public interface IFoo
{
   bool Bar(String id, params Object[] params);
}

或者

public interface IFoo
{
   bool Bar(String id, Dictionary<String, Object> params);
}

似乎前者对用户来说更容易调用,但后者的意图更明确,因为对于前者,您必须按特定顺序指定参数,以便实现正确解释它们,而对于后者你实际上是在做命名参数。

所以问题:

  • 我应该使用哪种形式(以及为什么?) - 其中一种被认为是最佳做法而不是另一种?
  • 我应该注意一种风格与另一种风格相比是否有特定的优势?其中之一是否被视为代码异味?
  • 是否有替代模式可以以不同/更好的方式实现相同的目标?

作为记录,我知道 .Net 4.0 中的 named parameters 但此代码需要在 .Net 3.5 上编译,因此不能使用任何 .Net 4.0+ 功能

编辑

只是为了更详细地说明我的 IFooBar() 方法实际上代表什么,因为有人问了。

IFoo 代表一些存储子系统,Bar() 实际上是一个创建操作。根据存储子系统,Bar() 可能不需要除 ID 以外的参数,也可能需要许多参数。

编辑 2

因此,为了回应@Kirk Woll 的评论和@Fernando 的回答,这里有更多信息。

我可能永远不会自己调用IFoo.Bar(),这个接口是开源框架的一部分。第 3 方开发人员将实现IFoo,最终用户将调用它的特定实例,拥有IFoo 的目的是让用户更容易在存储子系统之间迁移他们的应用程序,因为他们可以编码到接口而不是比具体的实现尽可能人性化。

在最简单的情况下,底层存储子系统只有一种存储形式,因此除了 ID 之外不需要其他参数。在复杂的情况下,存储子系统可以允许多种类型的存储,并且每种类型的存储可以允许任意复杂的配置参数集,例如索引大小、持久性、事务行为、索引策略、安全性和 ACL 考虑等。

我同意@Fernando 的观点,也许更多的多态性可能更有意义,也许多态性与泛型和类型限制相结合可能是最好的,例如

public interface IFoo
{
  bool Bar<T>(T parameters) where T : IBarConfig;
}

public interface IBarConfig
{
  String ID { get; set; }
}

然后用这样的实现:

public class MyFoo
{
  bool Bar<T>(T config) where T : MyBarConfig
  {
    //Implementation
  }
}

public class MyBarConfig : IBarConfig
{
  public String ID { get; set; }

  public long IndexSegmentSize { get; set; }

  //Etc...
}

这出乎我的意料,所以不确定在 MyFoo 中定义 Bar() 是否真的合法,其类型限制与其实现的接口不同?

【问题讨论】:

  • 除非您受限于 .Net Framework 的早期版本,否则我将始终使用通用版本。除非您处理大量数据,否则可重用代码的优势远远超过性能损失
  • 如果您需要知道您正在调用IFoo哪个 实现,以便决定传递Bar 的参数类型,那么您可能使用了错误的抽象. IFoo 的调用者应该与实现无关,但您的设计与此相反。您能否详细说明在更复杂的情况下需要传递什么样的参数?也许这些参数属于实现的属性,而不是方法的参数。
  • @Kirk Woll 为我的问题添加了更多细节

标签: c# .net params


【解决方案1】:

您需要决定是否需要搜索或能够从 params 集合/数组中检索对象。

当使用Object[] params 时,对象上没有索引。您需要迭代整个集合以找到一个项目(通过它的键)。

当使用Dictionary&lt;String, Object&gt; 时,您的对象将按其键进行索引,并且始终可以通过键轻松搜索/查询。

根据您的需要,您需要决定您的方法。

字典的搜索速度更快,但创建索引会产生开销。

【讨论】:

  • 还有更根本的区别。前者不能确保对(IEnumerable> 会,但又“不那么容易调用”。Dictionary 没有顺序......
【解决方案2】:

字典方法还有一个问题:拼写错误。您可能需要定义很多常量作为键来避免这个问题。

为什么不采用多态解决方案?

public interface IFoo {
    void Bar(FooData data);
}

public abstract class FooData {
     public int Id {get;set;}
}

public class MyFooData1 : FooData {
    public string SomeProperty {get;set;} 
    //...
}

public class MyFoo : IFoo {
    public void Bar(FooData data) {
        var myData = (MyFooData1)data;
        //...
    }
}

public class MyFooData2 : FooData {
    public int SomeOtherProperty {get;set;}
    //...
}

public class MyFoo2 : IFoo {
    public void Bar(FooData data) {
        var myData = (MyFooData2)data;
        //...
    }
}

你最终会得到更多更小的类,但它们很容易测试和扩展。

更新

@RobV 如果您正在实现接口,则无法更改类型限制,但是,如果您将类型参数放在接口声明中,您可能会完成您想要做的事情:

public interface IFoo<T> where T : IBarConfig {
    void Bar(T parameters);
}

public class MyBarConfig: IBarConfig {
    public String ID { get; set; }
    public long IndexSegmentSize { get; set; } 
}

public class MyFoo : IFoo<MyBarConfig> {
  public void Bar(MyBarConfig config) {
    //Implementation
  }
}

【讨论】:

  • 是的,考虑到我得到的各种答案和建议,我认为多态性与泛型和类型限制相结合可能是我的选择。请查看对问题的修改,看看您是否觉得合理
  • 当然,我过去实际上在接口级别做过,所以从经验中知道这是可行的
【解决方案3】:

如果您需要内联编写它们,与 Dictionary 相比,params 提供了更容易编写/阅读的代码。 IE。为每个对 String.Format 的调用构建字典 - 代码将不可读。另一方面,如果您已经有参数字典 - 可以使用它。

我建议重新考虑 API,看看您是否可以接受 IEnumerable,或者甚至更好的 IEnumerable&lt;T&gt; 作为参数。不幸的是,对于样本的这种通用 IFoo 名称,无法查看这种方法是否可行。

【讨论】:

  • 我假设您的意思是将其定义为Bar(String id, IEnumerable&lt;KeyValuePair&lt;String, Object&gt;&gt; parameters)?
  • 是的,如果您需要命名参数,否则如果您只有类似于 String.Format 的对象列表 - 尝试 IEnumerable 而不是 param。
【解决方案4】:

我应该使用哪种形式(以及为什么?) - 其中一种被认为是最佳实践而不是另一种? ---> 你应该使用第二个,因为它比第二个更不容易出错。

关于替代模式,肯定有一种方法可以更好地实现这一点。你能告诉我们你的问题实际上是什么吗?而不是使用 IFoo 和 Bar?除非我确切地知道您要做什么以及为什么...

【讨论】:

  • "你应该使用第二个,因为它比第二个更不容易出错。" - 我假设您的意思是用户第二个,因为第一个更容易出错?
【解决方案5】:

您提到可选参数的事实使我认为IDictionary&lt;string,object&gt; 方法会更好-这是在.Net 3.5 中提供这种接口的最佳方法。您可以只要求对象并执行类似于 MVC 对 htmlAttributes 所做的操作(使用反射将匿名对象转换为 IDictionary&lt;string,object&gt;)。

我更喜欢 params object[] 方法的情况是,在任何情况下都试图给他们起名字是很奇怪/不可能的,比如 string.format

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-01
    • 2018-01-25
    • 2012-05-13
    • 1970-01-01
    • 1970-01-01
    • 2015-04-12
    • 2022-01-23
    相关资源
    最近更新 更多