关于c#:在存储库接口中使用可选参数会被视为糟糕的设计吗?

Would using optional parameters in a repository interface be considered bad design?

我创建了两个类来外部控制存储库上的页码(Page和排序(Order)。存储库公开了IEnumerable而不是IQueryable,因为我希望存储库具有强大的可测试性。

例如,如果我有以下存储库:

1
2
3
4
interface IUserRepository
{
    public IEnumerable<User> GetAll();
}

如果我希望调用者控制响应的子集,他们将需要按页面/顺序传递。所以首先考虑的是添加重载:

1
2
3
4
5
interface IUserRepository
{
    IEnumerable<User> GetAll();
    IEnumerable<User> GetAllOrderedAndPaged(Order order, Page page);
}

我发现这个问题是,我必须创建多个重载,这是我发现非常繁琐的。

我想(但不确定)做的是让OrderPage作为可选参数:

1
2
3
4
interface IUserRepository
{
    IEnumerable<User> GetAll(Order order = null, Page page = null);
}

这样,调用代码可以很容易地进行排序/分页,但不能处理主查询(这是我试图通过公开IEnumerable而不是IQueryable来避免的)。

这个设计看起来是好是坏?我知道这可能有点主观,但我想看看这在功能上是否有问题。主要的关键是,我的存储库是可测试的,调用者不能过多地使用/更改查询。我认为,由于排序/排序对于返回的数据集来说是非常常见的任务,为什么不将它们合并到存储库接口的设计中呢?同样,在测试时,我只关心返回正确的数据集,但是调用方可以说"现在,给我这个页面/数据的有序子集"。


我会注意不要使用可选参数,因为默认值将被编译到调用者中。这意味着,如果默认值发生变化,任何使用原始默认值的代码都需要重新编译。

如果将来您决定null不是有效值,更改默认值,并在传入null时开始抛出ArgumentNullException,那么在您的特定示例中,这可能是一个问题。任何使用接口的代码都必须重新编译或可能开始抛出ArgumentNullExceptions

你可以在这里和这里读到更好的解释。

The key take-away from this: once you expose a default parameter value on a public method, you can never change it without recompiling all clients that depend on it. For library writers, this never means never ever.


我决定使用这两个参数作为存储库本身的属性。考虑到默认值将被编译到调用者中这一事实,而且在每个可能的存储库中的每个方法上写入这些参数可能会很麻烦。


对于我的编程风格,很多lambda的可选参数都不太适合。

C 4.0功能–命名和可选参数–呃,不,谢谢:http://blogs.ijw.co.nz/chris/index.php/2010/05/c-4-0-features-named-optional-parameters-uh-no-thanks/

作为更好的设计,我将使用元组作为参数。或用默认字段值聚合页和顺序的命名类。对不起,这当然是主观的。