当方法返回类型或属性类型为集合时,有些开发者会千篇一概地使用IList<T>集合。然而IList<T>具备集合的全部操做,这意味着调用者不只能够读取集合信息,还可以修改集合。业务需求原本只是为调用者提供一个可读的集合,例如数据的查询和展现,但当方法返回IList<T>时,无疑隐式地开放了集合可写的权限。此时,咱们没法阻止调用者篡改集合元素。html
public IList<Person> People{get; private set;}
,People属性是一个IList集合,它虽然是只读的,可是People集合里面的元素能够被修改。
这种状况下,咱们应该使用IEnumerable<T>来代替IList<T>。
IList<T>和IEnumerable<T>均可以遍历集合的元素,IList<T>拥有集合的全部操做方法,包括集合元素的增长、修改和删除。
而IEnumerable<T>则只有一个GetEnumerator方法(扩展方法除外),它返回一个可用于循环访问集合的IEnumerator<T>对象。ide
下面这段代码定义了两个类:Order和OrderLine。this
当调用者在取到Order实例时,仍然能够经过IList<T>的Add()或Remove()方法修改OrderLines集合。
假设调用者篡改了OrderLines中的元素,而且经过另外的方法回传了Order实例,则可能会产生一些bug。spa
/// <summary> /// 订单 /// </summary> public class Order { private readonly List<OrderLine> _orderLines = new List<OrderLine>(); private double _orderTotal; public IList<OrderLine> OrderLines { get { return _orderLines; } } public void AddOrderLine(OrderLine orderLine) { _orderTotal += orderLine.Total; _orderLines.Add(orderLine); } public void RemoveOrderLine(OrderLine orderLine) { orderLine = _orderLines.Find(item => item == orderLine); if (orderLine == null) return; _orderTotal -= orderLine.Total; _orderLines.Remove(orderLine); } } /// <summary> /// 订单明细 /// </summary> public class OrderLine { public double Total { get; set; } }
重构后,不只OrderLines集合是只读的,并且也只能经过AddOrderLine()和RemoveOrderLine()方法来增长或删除订单明细。code
/// <summary> /// 订单 /// </summary> public class Order { private readonly List<OrderLine> _orderLines; private double _orderTotal; public Order(List<OrderLine> orderLines) { _orderLines = orderLines; } /// <summary> /// 订单明细集合是只读的,只能经过AddOrderLine()和RemoveOrderLine()来增长或删除订单明细 /// </summary> public IEnumerable<OrderLine> OrderLines { get { return _orderLines; } } public double OrderTotal { get { return _orderTotal; } } public void AddOrderLine(OrderLine orderLine) { _orderTotal += orderLine.Total; _orderLines.Add(orderLine); } public void RemoveOrderLine(OrderLine orderLine) { orderLine = _orderLines.Find(item => item == orderLine); if (orderLine == null) return; _orderTotal -= orderLine.Total; _orderLines.Remove(orderLine); } } /// <summary> /// 订单明细 /// </summary> public class OrderLine { public double Total { get; set; } }
注意:上述代码有一些瑕疵,OrderLine类没有重写Object类的Equals()、GetHashCode()方法。
这行代码orderLine = _orderLines.Find(item => item == orderLine)会使得orderLine每次都是null。
较为完整的OrderLine以下:htm
public class OrderLine { public int Id { get; set; } public int OrderId { get; set; } public double Total { get; set; } /// <summary> /// 重写Equals方法 /// </summary> public override bool Equals(object obj) { if (obj == null || !(obj is OrderLine)) return false; if (ReferenceEquals(this, obj)) return true; var other = (OrderLine) obj; if (IsTransient() && other.IsTransient()) return false; var typeOfThis = GetType(); var typeOfOther = other.GetType(); if (!typeOfThis.IsAssignableFrom(typeOfOther) && !typeOfOther.IsAssignableFrom(typeOfThis)) { return false; } return Id.Equals(other.Id); } /// <summary> /// 重写GetHashCode方法 /// </summary> /// <returns></returns> public override int GetHashCode() { return Id.GetHashCode(); } /// <summary> /// 是否为瞬时对象 /// </summary> /// <returns></returns> public virtual bool IsTransient() { return EqualityComparer<int>.Default.Equals(Id, default(int)); } /// <summary> /// 提供==操做符,可用于对象比较 /// </summary> public static bool operator ==(OrderLine left, OrderLine right) { if (Equals(left, null)) { return Equals(right, null); } return left.Equals(right); } /// <summary> /// 提供!=操做用,可用于对象比较 /// </summary> public static bool operator !=(OrderLine left, OrderLine right) { return !(left == right); } }
当集合做为返回参数时,应使用适合业务需求的集合类型,不宜提供过多的集合操做给调用者。对象