公开接口而不是具体类

时间:2014-12-31 11:37:56

标签: c#

我最近在展示集合而不是具体实现(IEnumerable而不是List)时阅读了有关使用接口的内容。我现在在我的代码中尝试这样做。但是,当我公开一个返回IEnumerable的属性时,我很难不允许空值作为返回值。例如:

public class HumanResource
{
    public IEnumerable<EmployeeModel> Employees
    {
        get
        {
            // return what?
        }
    }
}

我应该在吸气剂中返回什么?我不想为此使用自动属性,因为我想避免空值。我想要的是返回一个没有项目的新集合。当然我可以返回任何实现IEnumerable的类型,但该类的外部用户将如何知道?或者我是否理解这个暴露界面而不是具体的实现错误?

编辑:删除了二传手

3 个答案:

答案 0 :(得分:5)

  

当然我可以返回任何实现IEnumerable的类型,但该类的外部用户将如何知道?

他们不必知道,这正是重点。

您的财产承诺会返回IEnumerable<EmplyeeModel>,而这正是发生的事情。

表示实现此接口的哪个类无关紧要。

  

我想要的是返回一个没有项目的新集合。

因此,Enumerable.Empty<EmplyeeModel>()new List<EmployeeModel>()会很好。


在设计API时,您需要考虑消费者对您返回的数据类型的处理方式,并相应地做出决定。

通常,集合的IEnumerable<T>适合所有人。当他们想要列表时,他们可以new List<T>(yourEnumerable)yourEnumerable.ToArray()将其用作数组。

答案 1 :(得分:1)

  

我想要的是返回一个没有项目的新集合。

属性可让您轻松完成此操作:

public class HumanResource
{
    // This is the real employees that gets returned when its not null
    private IEnumerable<EmployeeModel> employees; // may be null

    // This is the empty IEnumerable that gets returned when employees is null
    private static readonly IEnumerable<EmployeeModel> EmptyEmployees =
        new EmployeeModel[0];

    public IEnumerable<EmployeeModel> Employees
    {
        get
        {
            return employees ?? EmptyEmployees;
        }
        set {};
    }
}

employees变量设置为null时,代码返回一个空数组。您可以将employees设置为实现IEnumerable<EmployeeModel>的任何类型的集合,如果您愿意,甚至可以设置为数组。这是可能的,因为您通过界面返回。

当然,另一方面,客户端无法直接访问未通过接口公开的属性方法。例如,如果employees实际上是List,则调用者必须使用LINQ Count()而不是直接获取.Count。当然,您可以公开一个不同的界面,比如IList<EmployeeModel>,让您的客户使用其他方法。

答案 2 :(得分:0)

您仍需要为班级中的媒体资源提供内部支持集合。您可以在构造函数或字段声明中初始化集合:

public class HumanResource
{
   private readonly IList<EmployeeModel> _employees = new List<EmployeeModel>();

   public IEnumerable<EmployeeModel> Employees
   {
     get
     {
         return _employees;
     }
     // No Setter - callers may only enumerate the collection
  }
}

顺便说一句,请注意,即使你确实使用了自动属性(例如List<EmployeeModel>),它也会假定默认值为null,除非在别处初始化,所以在这方面没有任何变化。

编辑,回复:有什么好处?

  • 通过删除setter或将其设为私有,我们会阻止调用者重新分配HumanResource的内部集合
  • 通过将集合从List<>软化到IEnumerable<>,这意味着调用者只能对内部集合执行只读操作,例如迭代它。此外,IEnumerable<>可以在延迟迭代中使用,允许调用者在拥有所需数据后立即退出枚举。
  • 根据下面的评论,如果调用者需要在不同集合中表示的数据,例如Array,则LINQ扩展方法,例如.ToArray().ToList(),{{ 1}}可以使用。这样做会为调用者创建 new 集合,但会引用相同的.ToDictionary()个对象。这样做的性能损失很小。

最后要注意的是,将EmployeeModel属性上的setter设为私有,或者将后备字段声明为IEnumerable通常没有意义,因为这会阻止类本身使用impure操作集合的方法(即从中添加或删除对象),因为这样做需要强制转换,例如:

IEnumerable

我们在使用内部public class HumanResource { public IEnumerable<EmployeeModel> Employees { get; private set; } public HumanResource() { // Although the property can be assigned in the CTor to prevent the null issue ... Employees = new List<EmployeeModel>(); } private void AddProductiveEmployee() { // ... We need to continually cast within the class, which is just silly. (Employees as IList).Add(new EmployeeModel()); }

的手动支持字段方法时会遇到同样的问题
IEnumerable<>

TL; DR

  • 使用适合类的内部用法的集合类型(OO组合)
  • 但是在外部接口(例如公共getter / property)上,将内部实现隐藏到最低限度(OO封装)
  • 在构造函数(或内联)中初始化内部集合将阻止公开null集合