标签:
当方法返回类型或属性类型为集合时,有些开发者会千篇一律地使用IList<T>集合。然而IList<T>具有集合的所有操作,这意味着调用者不仅可以读取集合信息,还能够修改集合。业务需求本来只是为调用者提供一个可读的集合,例如数据的查询和展示,但当方法返回IList<T>时,无疑隐式地开放了集合可写的权限。此时,我们无法阻止调用者篡改集合元素。
public IList<Person> People{get; private set;} ,People属性是一个IList这种情况下,我们应该使用IEnumerable<T>来代替IList<T>。
IList<T>和IEnumerable<T>都可以遍历集合的元素,IList<T>拥有集合的所有操作方法,包括集合元素的增加、修改和删除。
而IEnumerable<T>则只有一个GetEnumerator方法(扩展方法除外),它返回一个可用于循环访问集合的IEnumerator<T>对象。
下面这段代码定义了两个类:Order和OrderLine。
当调用者在取到Order实例时,仍然可以通过IList<T>的Add()或Remove()方法修改OrderLines集合。
假设调用者篡改了OrderLines中的元素,并且通过另外的方法回传了Order实例,则可能会产生一些bug。
/// <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()方法来增加或删除订单明细。
/// <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如下:
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);
}
}
当集合作为返回参数时,应使用适合业务需求的集合类型,不宜提供过多的集合操作给调用者。
标签:
原文地址:http://www.cnblogs.com/keepfool/p/5548305.html