列表结果的开闭原则

Posted

技术标签:

【中文标题】列表结果的开闭原则【英文标题】:Open-closed principle for list result 【发布时间】:2018-12-10 11:08:31 【问题描述】:

我正在练习如何用 C# 创建一个开闭原则来返回一个信息列表。但问题是我的主要服务类出现错误。

这些是我的示例代码:

我有这个界面:

public interface ISelectInfo
    bool Rule(string rule);
    IQueryable<FoodDto> ReturnSearchResult(string query);

我有一个实现这个接口的类。

public class Fruit : ISelectInfo 
    public bool Rule(string rule) 
        return "fruit".Equals(rule); 
    
    public IQueryable<FoodDto> ReturnSearchResult(string query)
        // returns a list of FoodDto
    

我有这个主要的服务类

public class SelectTypeOFoodService 
    private readonly IList<ISelectInfo> selectInfo;
    public SelectTypeOfFruitService (IList<ISelectInfo> selectInfo)
        this.selectInfo = selectInfo;
    

    public async IEnumerable<FoodDto> SelectFood(string rule, string query)
        return await selectInfo.ToList().Where(x=>x.Rule(rule)).Select(x=>x.ReturnSearchResult(query)).AsQueryable().TolistAsync();
    

我的return await selectInfo.ToList()... 上出现错误和红色波浪线

我试图实现它以返回基于开放-封闭原则的 FoodDto 列表。

这是显示Cannot convert expression type 'System.Collections.Generic.List&lt;System.Linq.IQueryable&lt;FoodDto&gt;&gt;' to return type 'System.Collections.Generic.IEnumerable&lt;FoodDto&gt;'的红色曲线的示例结果

我希望有人可以帮助我。

【问题讨论】:

return await selectInfo.Where(x=&gt;x.Rule(rule)).Select(x=&gt;x.ReturnSearchResult(query)).AsQueryable().ToListAsync(); 这个问题与SOLID的OCP本身无关。都是关于 linq/IQueryable/TAP 的。此外,您应该提供完整的编译器错误消息,而不仅仅是“我得到一个红色曲线”。 @jsonGPPD 好的,我很困惑。这段代码的目的是什么?当参数是 IList 时,为什么要使用 Queryable 呢?最后,当数据已经在内存中时,为什么还要使用awaitToListAsync()?执行 IO 操作时,异步执行可防止阻塞。这里没有阻塞IO操作 @dymanoid 我在我的问题中添加了有关错误的附加信息 @jsonGPPD 这没有帮助。代码是自相矛盾的。你一开始想做什么?该代码试图解决什么问题? 【参考方案1】:

或者,您可以在您的 IQueryable&lt;FoodDto 上使用 async,查看下面的重构代码。

public class Fruit : ISelectInfo 
    public bool Rule(string rule) 
        return "fruit".Equals(rule); 
    
    public async Task<IEnumerable<FoodDto>> ReturnSearchResult(string query)
        // returns a list of FoodDto
    

为您效劳,

... 
public async Task<IEnumerable<FoodDto>> SelectFood(string rule, string query)
    return await selectInfo.FirstOrDefault(x=>x.Rule(rule)).Select(x=>x.ReturnSearchResult(query));

...

另外,不要忘记在中间件或启动类上注册接口和类。

希望这对其他人也有帮助。

【讨论】:

【参考方案2】:

我总是想知道为什么提问者给出一些代码并说代码不起作用,而没有给出确切的要求。

在我看来,您想向您的类 SelectTypeOFoodService 添加一个异步函数,该函数接受两个参数作为输入:string rulestring query

输出应该是this.selectInfos 中所有具有this.selectInfos.Rule(rule) 的真实返回值的项目的ReturnSearchResult(query)

注意:我冒昧地将您的收藏的标识符复数化,因为这样可以方便阅读答案。

可能是您简化了您的要求,但在我看来,您不需要为此使用异步函数。

IQueryable<FoodDto> QueryFood(string rule, string query)

    return this.selectInfos
        .Where(selectInfo => selectInfo.Rule(rule))
        .Select(selectInfo => selectInfo.ReturnSearchResult(query);


IEnumerable<FootDto> SelectFood(string rule, string query)

     return this.QueryFood(rule, query).AsEnumerable();

我选择使用AsEnumerable 而不是ToList,因为显然您想返回IEnumerable。如果您的来电者在致电您的SelectFood 之后使用FirstOrDefault,那么将您所有的一千种食物转换成一个列表然后只使用第一个将是一种浪费。

如果您确实需要调用 ToList,例如因为您要处理可查询对象,请考虑返回 List 而不是 IEnumerable。这将防止用户调用额外的ToList,从而导致您的项目的第二次列表化。

List<FoodDto> SelectFood(string rule, string query)

     return this.QueryFood(rule, query).ToList()

现在创建一个异步版本可能是有意义的,因为 ToList 是一个实际执行查询的函数,如果查询是由一个可等待的进程(如数据库查询、互联网获取数据、一个文件读取),那么创建一个异步函数可能是有意义的:

Task<List<FoodDto>> SelectFoodAsync((string rule, string query)

     return this.QueryFood(rule, query).ToListAsync();

再说一遍:如果没有什么可等待的,不要在类中引入 async-await,它只会让你的代码效率降低。

有时,您的类没有什么可等待的,但您确实需要创建一个异步函数,例如实现返回任务的接口,或在单元测试中模拟功能以测试使用 async-await 的功能.

这样的话,让async函数调用sync函数,用Task.FromResult组成返回值

Task<List<FoodDto>> SelectFoodAsync((string rule, string query)

    List<FoodDto> selectedFoods = this.SelectFoods(rule, query);
    return Task.FromResult(selectedFoods);

【讨论】:

以上是关于列表结果的开闭原则的主要内容,如果未能解决你的问题,请参考以下文章

如何使用反射满足工厂模式中的开闭原则?

C# 实例解释面向对象编程中的开闭原则

带构造函数的开闭原理

代码设计原则--开闭原则

七大设计原则之开闭原则应用

设计-七大原则