Discretion refactoring series [24]-encapsulation set, discretion refactoring
Overview
When the return type or attribute type of a method is set, some developers use the IList <T> set in the same way. However, IList <T> has all the operations on the set, which means that the caller can not only read the set information, but also modify the set. The business requirement is to provide a readable set for the caller, such as data query and display. However, when the method returns IList <T>, the set write permission is undoubtedly implicitly enabled. In this case, the caller cannot tamper with the set element.
Note: When the attribute is set to the IList <T> type, even if it is declared as read-only, we still cannot avoid tampering with the collection element.
For example
public IList<Person> People{get; private set;}The People attribute is an IList set. Although it is read-only, the elements in the People set can be modified.
In this case, we should use IEnumerable <T> instead of IList <T>.
Both IList <T> and IEnumerable <T> can traverse the elements of the set. IList <T> has all the operation methods of the Set, including adding, modifying, and deleting the set elements.
IEnumerable <T> only has one GetEnumerator method (except the extension method). It returns an IEnumerator <T> object that can be used to access the set cyclically.
Before reconstruction
The following code defines two classes: Order and OrderLine.
/// <Summary> /// Order /// </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 >/// order details /// </summary> public class OrderLine {public double Total {get; set ;}}After Reconstruction
After reconstruction, not only the OrderLines set is read-only, but also order details can be added or deleted through the AddOrderLine () and RemoveOrderLine () methods.
Hide code
/// <Summary> /// Order /// </summary> public class Order {private readonly List <OrderLine> _ orderLines; private double _ orderTotal; public Order (List <OrderLine> orderLines) {_ orderLines = orderLines;} // <summary> // The Order Details set is read-only and can only be passed through AddOrderLine () and RemoveOrderLine () to add or delete order details // </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 >/// order details /// </summary> public class OrderLine {public double Total {get; set ;}}
Note: The above code has some flaws. The OrderLine class does not overwrite the Equals () and GetHashCode () Methods of the Object class.
This line of code orderLine = _ orderLines. Find (item => item = orderLine) will make orderLine null every time.
The complete OrderLine is as follows:
Hide code
Public class OrderLine {public int Id {get; set;} public int OrderId {get; set;} public double Total {get; set ;} /// <summary> /// rewrite the Equals method /// </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> /// override the GetHashCode method /// </summary> /// <returns> </returns> public override int GetHashCode () {return Id. getHashCode ();} /// <summary> /// whether the object is an instantaneous object /// </summary> /// <returns> </returns> public virtual bool IsTransient () {return EqualityComparer <int>. default. equals (Id, default (int);} // <summ Ary> // provides the = operator for object comparison. // </summary> public static bool operator = (OrderLine left, OrderLine right) {if (Equals (left, null) {return Equals (right, null);} return left. equals (right) ;}/// <summary> // provided! = Operation, which can be used for object comparison // </summary> public static bool operator! = (OrderLine left, OrderLine right) {return! (Left = right );}}Summary
When the set is used as the return parameter, the set type suitable for the business needs should be used. Too many set operations should not be provided to the caller.
[Note] keepfool