Immutable collections, as the name implies, is that a collection cannot be modified. The data items of the collection are provided at the time of creation and are immutable throughout the life cycle.
Why do you use immutable objects? The immutable object has the following advantages:
- For unreliable customer code libraries, it is safe to use these objects safely in untrusted class libraries
- Thread-Safe: Immutable objects are safe under multiple threads, without race conditions
- There is no need to support variability, so you can save space and time overhead. All immutable collection implementations are more efficient at using memory than mutable collections (analysis)
- Can be used as a constant, and expect to remain unchanged in the future.
Immutable objects can be used naturally as constants because they are inherently immutable, and it is a good technical practice for defense programming (defensive programming) for the use of immutable objects.
Microsoft. NET team has officially released immutable collections that can be added through NuGet, including the following immutable collections:
System.Collections.Immutable.ImmutableArray
System.collections.immutable.immutablearray<t>
System.Collections.Immutable.ImmutableDictionary
System.collections.immutable.immutabledictionary<tkey,tvalue>
System.Collections.Immutable.ImmutableHashSet
System.collections.immutable.immutablehashset<t>
System.Collections.Immutable.ImmutableList
System.collections.immutable.immutablelist<t>
System.Collections.Immutable.ImmutableQueue
System.collections.immutable.immutablequeue<t>
System.Collections.Immutable.ImmutableSortedDictionary
System.collections.immutable.immutablesorteddictionary<tkey,tvalue>
System.Collections.Immutable.ImmutableSortedSet
System.collections.immutable.immutablesortedset<t>
System.Collections.Immutable.ImmutableStack
System.collections.immutable.immutablestack<t>
MSDN documentation Reference Https://msdn.microsoft.com/zh-cn/library/system.collections.immutable.aspx, how to use it? Let's take a look at an example, assuming you've built a billing system, you need an immutable design that doesn't need to worry about data corruption in the case of multithreaded operations. For example, you need to print a snapshot of the data through a worker thread, which avoids blocking the user's edits and allows the user to continue editing without affecting printing.
The mutable data model is this:
Class Order
{
Public Order ()
{
Lines = new list<orderline> ();
}
Public list<orderline> Lines {get; private set;}
}
Class Orderline
{
public int Quantity {get; set;}
Public decimal UnitPrice {get; set;}
Public float Discount {get; set;}
Public decimal Total
{
Get
{
return Quantity * UnitPrice * (decimal) (1.0f-discount);
}
}
}
Let's convert it to an immutable design:
Class Orderline
{
public orderline (int quantity, decimal UnitPrice, float discount)
{
Quantity = Quantity;
UnitPrice = UnitPrice;
Discount = Discount;
}
public int Quantity {get; private set;}
Public decimal UnitPrice {get; private set;}
Public float Discount {get; private set;}
Public decimal Total
{
Get
{
return Quantity * UnitPrice * (decimal) (1.0f-discount);
}
}
}
This new design requires you to create an order whenever any attribute value changes to create a new instance. You can add the withxxx method so that you can update a single property without explicitly calling the constructor :
Class Orderline
{
// ...
Public orderline withquantity (int value)
{
return value = = Quantity
? This
: New Orderline (Value, UnitPrice, Discount);
}
Public orderline Withunitprice (decimal value)
{
return value = = UnitPrice
? This
: New Orderline (Quantity, value, Discount);
}
Public orderline Withdiscount (float value)
{
return value = = Discount
? This
: New Orderline (Quantity, UnitPrice, value);
}
}
This makes the immutable use easier:
Orderline Apple = new Orderline (quantity:1, Unitprice:2.5m, discount:0.0f);
Orderline discountedappled = Apple. Withdiscount (. 3f);
Now let's see how we can implement the invariance of orders. The Lines property is already read-only, but it refers to a mutable object. Because it is a collection, it can easily be replaced by simply replacing Immutablelist <T> transforms:
Class Order
{
Public Order (ienumerable<orderline> lines)
{
Lines = Lines. Toimmutablelist ();
}
Public immutablelist<orderline> Lines {get; private set;}
Public Order Withlines (ienumerable<orderline> value)
{
Return Object.referenceequals (Lines, value)
? This
: New Order (value);
}
}
This design has some interesting properties:
• The constructor accepts IEnumerable <t>, which is allowed to be passed in any collection.
• We use the toimmutablelist () extension method to convert to Immutablelist <OrderLine>. If the instance is already an immutable list, it simply transforms instead of creating a new collection.
• The Withlines () method follows our order convention, and if the new list and current list are the same, you can avoid creating a new instance.
We can also add some handy ways to make it easier to update order lines:
Class Order
{
//...
Public Order AddLine (orderline value)
{
Return Withlines (Lines.add (value));
}
Public Order Removeline (orderline value)
{
Return Withlines (Lines.remove (value));
}
Public Order ReplaceLine (orderline oldValue, Orderline newvalue)
{
return OldValue = = NewValue
? This
: Withlines (Lines.replace (OldValue, newvalue));
}
}
The code for the supplemental order looks like this:
Orderline Apple = new Orderline (quantity:1, Unitprice:2.5m, discount:0.0f);
Order order = New Order (Immutablelist.create (Apple));
Orderline discountedapple = Apple. Withdiscount (discount);
Order Discountedorder = order. ReplaceLine (Apple, discountedapple);
The advantage of this design is that it avoids unnecessary object creation as much as possible. For example, when the value of the discount equals 0.0 f, there is no immediate discount, and Discountedapple and Discountedorder refer to the existing instance of Apple and the order.
This is because:
1.apple. Withdiscount () will return an existing instance of Apple because the new discount is the current value of the same discount attribute.
2.order. ReplaceLine () If all two parameters are the same, the existing instance is returned.
Our constant collection of other operations follows this maximization of reuse. For example, an order that adds an order line to a 1000 order line and 1,001 order lines does not create an entire new list. Instead, it reuses a chunk of the existing list. This is possible because the list internal structure is for a tree, allowing nodes to share different instances.
Here are two videos to introduce the variability collection:
Immutable collections for. NET
Inner workings of immutable collections
Series of immutable Collections blog recommendations:
Exploring the. NET Corefx part 9:immutable collections and the Builder
Exploring the. NET Corefx part 13:immutablelist are an AVL Tree
Exploring the. NET Corefx part 14:inside immutable collections
Immutable (immutable) collection