[英]Exposing interfaces instead of concrete classes
I've recently read something about using interfaces when exposing collections instead of concrete implementations (IEnumerable instead of List). 我最近阅读了一些有关在公开集合而不是具体实现时使用接口的信息(IEnumerable而不是List)。 I'm trying to do that now in my code.
我现在正在尝试在我的代码中执行此操作。 However, when I expose a property that return IEnumerable, I'm having some difficulty of not allowing nulls as a return value.
但是,当我公开一个返回IEnumerable的属性时,我遇到了一些困难,即不允许将空值作为返回值。 Example:
例:
public class HumanResource
{
public IEnumerable<EmployeeModel> Employees
{
get
{
// return what?
}
}
}
What should I return in the getter? 我应该在吸气剂中返回什么? I don't want to use automatic properties for this as I want to avoid nulls.
我不想为此使用自动属性,因为我想避免使用null。 What I want is to return a new collection with no items.
我想要的是退回没有任何物品的新收藏。 Of course I can return any type that implements IEnumerable but how will the external user of the class know that?
当然,我可以返回实现IEnumerable的任何类型,但是该类的外部用户将如何知道呢? Or did I understand this exposing interface instead of concrete implementations wrong?
还是我了解这个公开的界面,而不是错误的具体实现?
EDIT: Removed setter 编辑:删除二传手
Of course I can return any type that implements IEnumerable but how will the external user of the class know that?
当然,我可以返回实现IEnumerable的任何类型,但是该类的外部用户将如何知道呢?
They don't have to know that, that's exactly the point. 他们不必知道这一点,这就是重点。
Your property promises to return an IEnumerable<EmplyeeModel>
, and that's exactly what happens. 您的媒体资源承诺会返回
IEnumerable<EmplyeeModel>
,而这正是发生的情况。 It doesn't matter which class implementing this interface your code returns. 您的代码返回哪个实现此接口的类都没有关系。
What I want is to return a new collection with no items.
我想要的是退回没有任何物品的新收藏。
So, Enumerable.Empty<EmplyeeModel>()
or new List<EmployeeModel>()
will do just fine. 因此,
Enumerable.Empty<EmplyeeModel>()
或new List<EmployeeModel>()
都可以。
When designing an API you need to think about what the consumers will do with the data types you return, and decide upon that accordingly. 在设计API时,您需要考虑使用者将如何处理返回的数据类型,并据此做出决定。
Usually an IEnumerable<T>
for collections suits everyone. 通常,集合的
IEnumerable<T>
适合每个人。 When they want it in a list, they can do new List<T>(yourEnumerable)
, or yourEnumerable.ToArray()
to use it as an array. 当他们想要它在列表中时,他们可以执行
new List<T>(yourEnumerable)
或yourEnumerable.ToArray()
以将其用作数组。
What I want is to return a new collection with no items.
我想要的是退回没有任何物品的新收藏。
Properties let you do that very easily: 属性使您可以很容易地做到这一点:
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 {};
}
}
The code returns an empty array when employees
variable is set to null
. 当
employees
变量设置为null
时,代码将返回一个空数组。 You can set employees
to a collection of any type that implements IEnumerable<EmployeeModel>
, or even to an array if you prefer. 您可以将
employees
设置为实现IEnumerable<EmployeeModel>
的任何类型的集合,或者根据需要设置为数组。 This is possible because you return by interface. 这是可能的,因为您通过接口返回。
The flip side of this, of course, is that the clients would have no direct access to methods of properties that are not exposed through the interface. 当然,另一方面,客户端将无法直接访问未通过接口公开的属性方法。 For example, if
employees
is actually a List
, the callers would have to use LINQ's Count()
instead of obtaining .Count
directly. 例如,如果
employees
实际上是一个List
,则调用者将不得不使用LINQ的Count()
而不是直接获取.Count
。 Of course you can expose a different interface, say, IList<EmployeeModel>
, to let your clients use additional methods. 当然,您可以公开另一个接口,例如
IList<EmployeeModel>
,以使您的客户端使用其他方法。
You still need to provide an internal backing collection for the property in your class. 您仍然需要为类中的属性提供内部支持集合。 You can initialize the collection in the constructor, or in the field declaration:
您可以在构造函数中或在字段声明中初始化集合:
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
}
}
As an aside, note that even if you did use an automatic property (eg List<EmployeeModel>
), that it would assume a default value of null, unless otherwise initialized elsewhere, so nothing changes in this respect. 顺便说一句,请注意,即使您确实使用了自动属性(例如
List<EmployeeModel>
),它也会采用默认值null,除非在其他地方进行了初始化,所以这方面没有任何改变。
Edit, Re : What are the benefits? 编辑,回复:有什么好处?
HumanResource
HumanResource
的内部集合。 List<>
to an IEnumerable<>
, it means the caller can only do read-only actions on the internal collection, such as to iterate it. List<>
IEnumerable<>
为IEnumerable<>
,这意味着调用方只能对内部集合执行只读操作,例如对其进行迭代。 In addition, IEnumerable<>
can be used in a lazy iteration, allowing the caller to quit enumerating as soon as it has the data it needs. IEnumerable<>
可以用于延迟迭代中,从而允许调用方在拥有所需数据后立即退出枚举。 Array
, then LINQ extension methods such as .ToArray()
, .ToList()
, .ToDictionary()
can be used. Array
表示的数据,则可以使用LINQ扩展方法,例如.ToArray()
.ToList()
和.ToDictionary()
。 Doing so will create new collections for the caller, but with references to the same EmployeeModel
objects. EmployeeModel
对象。 The performance penalties of doing this are minimal. One final note is that there is usually no point in making the setter on an IEnumerable
property private, or declaring the backing field as an IEnumerable
, as this will prevent the class itself from using impure methods to manipulate the collection (ie add or remove objects from it), as doing so would require a cast, eg: 最后一点要注意的是,通常没有必要将
IEnumerable
属性的setter设置为私有,或者将后备字段声明为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());
}
We would have the same problem with the manual backing field approach with an internal IEnumerable<>
使用内部
IEnumerable<>
的手动支持字段方法会遇到相同的问题
// Unlikely to be useful
private readonly IEnumerable<EmployeeModel> _employees = new List<EmployeeModel>();
TL;DR TL; DR
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.