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 中的命名参数,但此代码需要在 .Net 3.5 上编译,因此不能使用任何 .Net 4.0+ 功能

编辑

只是为了添加更多关于我IFooBar()方法实际代表的细节,因为有人问过。

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

编辑 2

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

我可能永远不会调用IFoo.Bar()自己,这个接口是开源框架的一部分。3rd 方开发人员将实施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...
}

这不是我的想法,所以不确定用不同的类型限制定义是否真的合法,Bar()然后MyFoo是它实现的接口?

4

5 回答 5

4

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

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

使用 a 时Dictionary<String, Object>,您的对象由它们的键索引,并且它总是很容易通过键搜索/查询。

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

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

于 2012-07-07T00:05:34.113 回答
2

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

我建议重新考虑 API,看看你是否可以接受IEnumerable,甚至更好地IEnumerable<T>作为参数。不幸的是,使用这种通用的 IFoo 示例名称,无法查看这种方法是否有效。

于 2012-07-07T00:19:44.103 回答
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
  }
}
于 2012-07-07T01:39:17.110 回答
0

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

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

于 2012-07-07T00:19:23.547 回答
0

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

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

于 2012-07-07T01:22:10.340 回答