Operate Just-In-Time
Assume we wrote the following definition for a SalesInvoiceLine
class:
public class SalesInvoiceLine { public string Product { get; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } public decimal Subtotal { get; } public SalesInvoiceLine(string product, decimal unitPrice, int quantity) { Product = product; UnitPrice = unitPrice; Quantity = quantity; Subtotal = UnitPrice * Quantity; } }
The following unit test verifies the correctness of the Subtotal
property:
[Fact] public void Construct_SalesInvoiceLine_Check_Subtotal() { var line = new SalesInvoiceLine("Apple", 0.35m, 3); line.Subtotal.Should().Be(1.05m); // PASSES }
The test passes. All is good. Or is it?
Can you see a problem with the definition of SalesInvoiceLine
?
Let’s add another unit test:
[Fact] public void Update_Quantity_SalesInvoiceLine_Check_Subtotal() { var line = new SalesInvoiceLine("Apple", 0.35m, 3); line.Subtotal.Should().Be(1.05m); line.Quantity = 7; line.Subtotal.Should().Be(2.45m); // FAILS! }
We construct an instance of SalesInvoiceLine
the same as before. Now though, we are increasing the Quantity
to 7.
The unit test fails. Why?
The unit test fails with this message: “Expected line.Subtotal to be 2.45m, but found 1.05m.”
It fails because we calculate the Subtotal
property in the constructor.
Once again, here is the listing of SalesInvoiceLine with the problematic code bolded:
public class SalesInvoiceLine { public string Product { get; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } public decimal Subtotal { get; } public SalesInvoiceLine(string product, decimal unitPrice, int quantity) { Product = product; UnitPrice = unitPrice; Quantity = quantity; Subtotal = UnitPrice * Quantity; } }
Properties Quantity
and UnitPrice
have setters; that means we can change them. SalesInvoiceLine
instances are mutable—their internal state can vary.
As the code stands, when the UnitPrice
or Quantity
changes after object construction, it will not affect the Subtotal
. We have fixed the Subtotal
at construction time!
So how do we overcome this issue?
We want to calculate the Subtotal
as late as possible—ideally, when we are calling it. In that way, we capture the latest values for the component factors, UnitPrice
and Quantity
.
The solution? Turn Subtotal
into a calculated read-only property:
public class SalesInvoiceLine { public string Product { get; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } public decimal Subtotal => UnitPrice * Quantity; public SalesInvoiceLine(string product, decimal unitPrice, int quantity) { Product = product; UnitPrice = unitPrice; Quantity = quantity; } }
Since we no longer needed the calculation of Subtotal
in the constructor, we have dropped that too. The compiler complained about setting a read-only property; we had to remove it.
This technique works well with calculated values, formatting strings and other simple procedures. It doesn’t work well in all situations. When operations are resource-intensive or take a long time, you may find that frequent calls cause performance problems. In that case, you may want to consider other solutions.
Leave a Reply
Want to join the discussion?Feel free to contribute!