4

我刚刚开始研究 .NET 3.5,所以如果之前有人问过这类问题,请原谅我。我正在为扩展方法的体面使用而苦苦挣扎,因为我刚刚下载了 suteki shop 一个 MVC 电子商务产品。在这个项目中,有一个非常标准的存储库模式,它扩展了 IRepository。

为了扩展此接口公开的基本功能,使用了扩展方法,即:

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

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

存储库已被接口的事实表明尝试采用与数据层无关的方法。也就是说,如果我要创建自己的数据层,我会很困惑我必须创建哪些扩展方法来确保我已经满足了对实现我的存储库类的类的合同要求。

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

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

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

编辑:

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

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

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

4

4 回答 4

5

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

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

我不认为“旧”方式真的是一个接口扩展IRepository<Category>——它是一个普通的静态方法IRepository<Category>。这就是扩展方法让生活更愉快的地方。如果您真的会使用继承,那么现在也可以这样做。不幸的是,我们对您的系统架构的了解还不够,无法肯定地说。

于 2008-11-12T21:01:52.297 回答
4

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

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

于 2009-10-09T07:13:43.790 回答
1

这不是一回事。扩展方法只是一种语法便利,仅此而已,当然也不是合同要求。您不会在自己的课程中“实施”它们。对于上面的示例:

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

与以下内容完全相同

Category c = CategoryRepositoryExtensions.GetRootCategory(r);

因此对接口的实现者没有额外的要求。

于 2008-11-12T21:03:02.667 回答
0

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

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

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

  • 场景 2:获取包含产品 x 并已运送到邮政编码 y 的所有订单的列表

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

方案 1

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

方案 2

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

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

希望这可以帮助

于 2008-11-12T21:31:22.500 回答