From C# objects to CSV With System.Text.Json
In this article, I discuss the process of binding data from a request to the PageModel. Model binding in ASP.NET...
In C#, there are reference types and value types. Value types are variables assigned to data that represent an object’s unique instance. With reference types, the variable points to a reference of an object. In this case, the variable is only a representation of the object. This means that if you modify a variable that points to a reference type, you will modify the value that the variable is pointing to. In this post, I will demonstrate this concept using an example of a Pizza order. I will then show you how to make a deep copy of a reference type using JSON serialization.
Let’s define some classes (reference types) for our pizza order.
public class Pizza
{
public string Size {get; set;}
public List<string> Toppings {get; set;}
public bool ExtraCheese {get; set;}
}
Now, we can use these classes to create our Pizza order
var pizzaOne = new Pizza()
{
Size = "XL",
Toppings = new List<string>{"pepporoni", "sausage", "mushroom"},
ExtraCheese = true,
}
var pizzaTwo = new Pizza()
{
Size = "XL",
Toppings = new List<string>{"ham", "sausage", "bacon"},
ExtraCheese = true
}
var order = new List<Pizza>()
{
pizzaOne,
pizzaTwo
};
I just heard that more friends are coming to our Super Bowl party. I need to order another XL Pizza with pepperoni, sausage, mushroom, and extra cheese (pizza one
).
So, the pizza shop updated my order, and the following code was executed to make the change.
var pizzaThree = pizzaOne;
Since I ordered the same pizza, the program executes code that sets a pizzaThree
variable to pizzaOne
. pizzaThree
is not a unique pizza order. It is a reference to the pizzaOne. We can use Object.Equals()
to confirm the two object instances are equal.
Console.WriteLine(Object.Equals(pizzaOne, pizzaThree)); // True
You may be wondering why this is a problem. I did order the same pizza. It should be the same in the context of the program. The program needs to recognize my new pizza as a new object instance. Why? Suppose I changed my mind and called the pizza shop to change my order. I don’t want extra cheese on the last pizza I ordered (pizzaThree
). So, the logic in the code is written to update the pizzaThree
object instance as follows.
pizzaThree.ExtraCheese = false;
Now, let’s print my whole order to the console.
Console.WriteLine($"pizzaOne: {pizzaOne.Size} pizza with {string.Join(", ", pizzaOne.Toppings)}. Extra Cheese? {pizzaOne.ExtraCheese}");
Console.WriteLine($"pizzaTwo: {pizzaTwo.Size} pizza with {string.Join(", ", pizzaTwo.Toppings)}. Extra Cheese? {pizzaTwo.ExtraCheese}");
Console.WriteLine($"pizzaThree: {pizzaThree.Size} pizza with {string.Join(", ", pizzaThree.Toppings)}. Extra Cheese? {pizzaThree.ExtraCheese}");
pizzaOne: XL pizza with pepperoni, sausage, mushroom. Extra Cheese? False
pizzaTwo: XL pizza with ham, sausage, bacon. Extra Cheese? True
pizzaThree: XL pizza with pepperoni, sausage, mushroom. Extra Cheese? False
So now you can visualize the bug, when we changed pizzaThree
to not have extra cheese, the program also changed pizzaOne
to not have extra cheese.
That’s because when initially declared the pizzaThree
variable, we did not set it to a new instance of Pizza
we set it to a reference of pizzaOne
when means what every changes we make to the pizzaThree
instance will be sent to the pizzaOne
instance.
Let’s fix this bug by creating a deep copy of pizzaOne
to create the pizzaThree
instance.
Serialize the object we want to make the deep copy of:
using System.Text.Json;
var serializedPizzaOne = JsonSerializer.Serialize(pizzaOne);
Then, we can deserialize the object to the correct type and assign the object to the pizzaThree
variable:
using System.Text.Json;
pizzaThree = JsonSerializer.Deserializer<Pizza>(serializedPizzaOne);
Great, now let’s not forget we don’t want extra cheese on pizzaThree
:
pizzaThree.ExtraCheese = false;
Now let’s see if pizzaOne and pizzaThree
are still equal:
Console.WriteLine(Object.Equals(pizzaOne, pizzaThree)); // False
Perfect, we expect these objects to be two different instances of the Pizza
class.
Let’s check the order to ensure that pizzaThree
is the same as pizzaOne
but without extra cheese.
Console.WriteLine($"pizzaOne: {pizzaOne.Size} pizza with {string.Join(", ", pizzaOne.Toppings)}. Extra Cheese? {pizzaOne.ExtraCheese}");
Console.WriteLine($"pizzaTwo: {pizzaTwo.Size} pizza with {string.Join(", ", pizzaTwo.Toppings)}. Extra Cheese? {pizzaTwo.ExtraCheese}");
Console.WriteLine($"pizzaThree: {pizzaThree.Size} pizza with {string.Join(", ", pizzaThree.Toppings)}. Extra Cheese? {pizzaThree.ExtraCheese}");
Console
pizzaOne: XL pizza with pepperoni, sausage, mushroom. Extra Cheese? True
pizzaTwo: XL pizza with ham, sausage, bacon. Extra Cheese? True
pizzaThree: XL pizza with pepperoni, sausage, mushroom. Extra Cheese? False
It worked! By creating a deep copy of our reference type, we could update the ExtraCheese property of the pizzaThree
instance without affecting the pizzaOne
instance from which it was copied.
In this post, I shared a simple pizza order example to demonstrate reference types. Then, I showed you how to make a deep copy of a reference type using JSON serialization. Thanks for reading!