扩展方法的用法 - 这不是一个糟糕的设计吗?

时间:2008-11-12 20:43:54

标签: architecture .net-3.5 extension-methods

我刚刚开始研究.NET 3.5,如果之前已经提出这类问题,请原谅我。我正在努力使用扩展方法,因为我刚刚下载了suteki商店的MVC电子商务产品。在这个项目中,有一个非常标准的Repository模式,它扩展了IRepository。

为了扩展该接口所暴露的基本功能,使用了扩展方法,即:

public static class CategoryRepositoryExtensions
{
    public static Category GetRootCategory(this IRepository<Category> categoryRepository)
    {
     return categoryRepository.GetById(1);
    }
}

现在这一切都很好,但就我而言,Interfaces充当了实现它们的对象的契约。

存储库已经接口的事实表明尝试采用数据层不可知的方法。也就是说,如果我要创建自己的数据层,我会对我必须创建的扩展方法感到困惑,以确保我已经满足了对实现我的存储库类的类的合同要求。

似乎创建IRepository然后扩展它的旧方法可以更好地查看所需内容,例如。

ICategoryRepoitory : IRepository<Category>
{
     Category GetRootCategory();
}

所以我想我的问题是,对于其他人来说,使用Extention方法似乎是错误的吗?如果没有,为什么?我不应该抱怨这个吗?

编辑:

以上示例似乎是一个很好的例子,说明为什么扩展方法非常有用。

我想我的问题是,如果数据访问特定的实现被卡在数据访问机制程序集中的扩展方法中。

这样,如果我将其换成另一种机制,我将不得不在该程序集中创建一个类似的扩展方法。

4 个答案:

答案 0 :(得分:5)

关键是如果你已经适当地实现了所有IRepository<T>,那么你(作为数据层实现者)根本不必知道根类别。对于此扩展方法的范围,假设类别的任何存储库将具有ID 1的根类别。这可能是一个完全合理的假设,并且是一个有用的假设:没有人有构建一个派生类(这可能是不切实际的 - 数据层可能有工厂等,这使得从实现派生出来很棘手)。

现在,扩展方法仅适用于其范围适当的情况 - 如果它位于命名空间(或密切相关的范围)中,其中关于根类别的假设将是有效的。

我不认为“较旧”的方式实际上是一个扩展IRepository<Category>的接口 - 它将是一个采用IRepository<Category>的常规静态方法。这就是扩展方法让生活更愉快的地方。如果你真的会使用继承,那么现在也可以这样做。遗憾的是,我们对您系统的体系结构了解不足以确定。

答案 1 :(得分:4)

扩展方法概念只是一些作者所称的语法糖。它使代码更具可读性,但不易理解。最终,扩展方法只是静态方法,是程序范式的遗产。它们使代码紧密耦合,内聚力更低,更难以测试和重用。

我对C#编程语言的这种倾向有偏见。这个功能很有吸引力但它没有带来任何好处,相反它增加了代码的复杂性。

答案 2 :(得分:1)

这不是一回事。扩展方法是一种语法上的便利,仅此而且当然不是合同要求。你不要在自己的课上“实施”它们。对于上面的例子:

using CategoryRepositoryExtensions;
...
Category c = r.GetRootCategory();

完全相同:

Category c = CategoryRepositoryExtensions.GetRootCategory(r);

因此对接口的实现者没有其他要求。

答案 3 :(得分:0)

是的,在您提到的示例中,它看起来似乎违反直觉,原因是您在单个级别上处理单个对象。我发现当您使用IQueryable / IEnumerable集合时,扩展方法最有用。

例如。让我们考虑两种情况:

  • 场景1:获取包含产品x的所有订单的列表

  • 场景2:获取包含产品x且已经发送到zipcode y的所有订单的列表

如果您使用的是传统/接口驱动方法,您可能会定义2可以定义2个不同的接口。如果您使用扩展方法,则可以使用类似以下内容的

情景1

<IEnumerable> orders1Enumerable = OrderRepository.GetAllOrders()
                                     .ContainingProductCode(x);

情景2

<IEnumerable> orders2Enumerable = OrderRepository.GetAllOrders()
                                     .ContainingProductCode(x)
                                     .WithShippingZipCode(y);

执行此操作时,框架会通过链接这些扩展方法来动态创建适当的SQL

希望这有帮助