程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 小酌重構系列[24]——封裝集合,小酌重構

小酌重構系列[24]——封裝集合,小酌重構

編輯:C#入門知識

小酌重構系列[24]——封裝集合,小酌重構


概述

當方法返回類型或屬性類型為集合時,有些開發者會千篇一律地使用IList<T>集合。然而IList<T>具有集合的所有操作,這意味著調用者不僅可以讀取集合信息,還能夠修改集合。業務需求本來只是為調用者提供一個可讀的集合,例如數據的查詢和展示,但當方法返回IList<T>時,無疑隱式地開放了集合可寫的權限。此時,我們無法阻止調用者篡改集合元素。

注意:將屬性設定為IList<T>類型時,即使聲明為只讀的,我們仍然無法避免集合元素的篡改。
例如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>對象。

示例

重構前

下面這段代碼定義了兩個類:Order和OrderLine。

/// <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);
    }
}

小結

當集合作為返回參數時,應使用適合業務需求的集合類型,不宜提供過多的集合操作給調用者。

【關注】keepfool

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved