何时应该使用 params Object[] 而不是 Dictionary<String, Object>?

5
我将API定义为一个接口,我们将其称为IFoo,并且我想定义一个方法Bar()
这个方法Bar()将会带有一个必需的参数,以及一些任意数量的其他参数。这些其他参数的解释将由IFoo的实现者来决定。
对于这种情况,使用params还是Dictionary<String, Object>来定义我的接口更为合适?
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(),因为这个接口是开源框架的一部分。第三方开发人员将实现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,否则我会始终选择通用版本。可重用代码的优势远远超过性能损失,除非你处理大量数据。 - JMK
如果您需要知道在调用IFoo的哪个实现以决定传递什么类型的参数到Bar,那么您可能使用了错误的抽象。调用IFoo的程序应该对实现是不加考虑的,但是您的设计却相反。在更复杂的情况下需要传递哪些参数?也许这些参数应该作为实现的属性而不是方法的参数。 - Kirk Woll
@Kirk Woll 我在我的问题中添加了更多细节。 - RobV
5个回答

4
您需要决定是否需要从params集合/数组中搜索或检索对象。
使用Object[] params时,对象没有索引。您需要遍历整个集合以查找项目(通过其键)。
使用Dictionary时,您的对象按它们的键进行索引,并且总是可以通过键进行轻松搜索/查询。
根据您的需求,您需要决定您的方法。
字典对于搜索更快,但创建索引会有开销。

有更根本的区别。前者不保证成对(IEnumerable<KeyValuePair<String,Object>>会,但“不容易调用”)。而Dictionary<String,Object>没有顺序... - user166390

2

如果您需要在代码中内联编写参数,与字典相比,参数提供了更易于编写/阅读的代码。例如,为每个对String.Format的调用构建字典将使代码难以阅读。另一方面,如果您已经拥有参数字典,则可以使用它。

我建议重新考虑API,并查看是否可以接受IEnumerable或甚至更好的IEnumerable<T>作为参数。不幸的是,由于示例的IFoo名称过于通用,因此无法确定这种方法是否可行。


我猜你是指将它定义为 Bar(String id, IEnumerable<KeyValuePair<String, Object>> parameters) - RobV
1
如果你需要命名参数,则使用命名参数。否则,如果你只有类似于 String.Format 的对象列表,请尝试使用 IEnumerable<Object> 而不是 param。 - Alexei Levenkov

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

是的,我认为在我这种情况下,多态性结合泛型和类型限制可能是一个可行的解决方案,考虑到我得到的各种答案和建议。请查看问题的修改,并看看您是否认为这样合理。 - RobV
当然,我以前在接口层面上就做过这样的事情,所以根据经验,这是可行的。 - RobV

0

我应该使用哪种形式(以及为什么)-这些中是否有一种被认为是最佳实践?--->您应该使用第二种,因为它比第一种容错性更强。

关于替代模式,肯定有更好的实现方式。你能告诉我们你的问题实际上是什么吗?而不是使用IFoo和Bar?除非我确切地知道你要做什么以及为什么,否则我无法提出其他建议...


1
你应该使用第二个,因为它比第一个更少容易出错。- 我认为你的意思是使用第二个,因为第一个更容易出错? - RobV

0
提到可选参数,让我想到使用 IDictionary<string,object> 方法可能更好——这是在 .Net 3.5 中提供此类接口的最佳方法。你也可以要求 object 类型,然后像 MVC 中的 htmlAttributes 一样使用反射将匿名对象转换为 IDictionary<string,object>
而对于那些在每种情况下都需要命名的场景,例如 string.format,我更喜欢使用 params object[] 方法,因为在这种情况下强制命名会显得很奇怪或不可能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接