Memory Structure Shallow and Deep Copy in .Net 5

Bora Kaşmer
7 min readJul 14, 2021

Today we will talk about memory management, conscious and unconscious type conversion, and junior’s most common mistakes shallow copy in C#.

“One of the keys to happiness is a bad memory.”
Rita Mae Brown

We can keep variables and objects in two types of memory in C#. We call them Heap (Reference Type) and Stack (Value Type) memory. If you use primitive types or, most importantly, if you know the length of variables, you can use Stack memory, but if you don’t know the exact length of the variables, you must use Heap memory.

Stack Memory requires a reservation. It is kept in Rem and too fast. An accurate location is found with Stack Pointer. There is no need to allocate a specific place for the heap. It is also stored in Rem. Dynamic space is allocated at runtime, not compile time. But unfortunately, it is slow.

You must be careful when you use Static variables. They are stored on the Managed Heap, not the Stack when the type is first referenced. And everyone can access them.

In C#, you must always set the value to the variable. In the below example, the compiler throws an error. Because “a” variable is not set a value yet.

int a;

A variable name doesn’t start with a number. It does not contain spaces. There is a capitalization difference. You can use variables in whichever block it is defined in. Out of the scope, variables can’t be used. These variables are disposed of when you go out of scope.

{ int a=5; } { int b=7; }

In C# “new” means, assigning the initial value. “int a =new int(); bool b= new bool();” If you write the “a” value to the screen, you can see “0” and if you write the “b” value, you can see “false” result on the console.

Boxing: Matching an object with the Object data type is called Boxing. “c” variable, keeps in Stack Memory but “d” variable, keeps in Heap Memory. And “d” variable’s pointer address keeps in Stack Memory.

Unconscious type conversion: It is an easy and automatic type conversation by C#. Below the example, when we set “a” to “b”, it automatically converted float type. But not permanent.

The small type turns into the big type. But the big type cannot become the small type. It cannot be unconsciously. In the below example, when we set “b” to “a”, the “float” type “b” can not be converted to “int” automatically. Visual Studio gave an error and underlined to the “b”.

This is interesting converting. In the below example, when we set char “c” to double “b”, the “char” type “c” converted to “double” automatically. And the result is 97. This is the ASCII code of “a” char.

In the below example, we try to set add two bytes to one byte. Of course, it is impossible because two bytes don’t fit in one byte :)

In conscious type conversion, the Bigger type can be assigned to the Small type. But this is not possible in unconscious type conversion. For this, we have to force ourselves.

In the below example, unconscious int “a” can not convert to byte. Because of int bigger then byte. “1 int is 4 byte.” So we have to force “a” to convert to “byte”. So we used two methods. “Cast (byte)” and “Convert.ToByte()” method.

8 bites are 1 byte. And 4 bytes is 1 int. In the below example, we see bites conversion of “10”. So when we convert “10” to byte, there is no problem because there is enough space for “10” in 1 byte.

int i= 10
Byte b= (byte)i => 00000000 00000000 00000000 00001010

In the below example, we see a max length of 1 byte. This is “255”. What about “256”?

int i= 255
Byte b= (byte)i => 00000000 00000000 00000000 11111111

If the “i” variable was “256”, you could not fit it in 1 byte, so you need 2 bytes, as seen below. Finally the value of the “b” variable is “0”. For this mistake, C# doesn’t allow assigned bigger type to the small type unconscious. If you want, you have to force it conscious.

int i= 256
Byte b= (byte)i => 00000000 00000000 00000001 00000000

“We do not remember days; we remember moments.“ ―Cesare Pavese

Strange Story of UnBoxing:

If you try to cast “Object” to “int”, you moved “a” variable from Heap memory to Stack memory. Because Object keeps in Heap, but int keeps in Stack memory.

Object a = 1923;
int b = (int)a;

String is a reference type, but it is immutable. This is so strange. Because when we assigned a value, it cannot be changed. If we try to change a string value, the compiler creates a new string object in the Heap memory and point a variable in Stack Memory to show the new memory location in Heap.

Deep and Shallow Copy

This is the most making mistake by juniors at the beginning. Before trying to understand deep & shallow copy, you must know Value and Reference Memory Type. Everything is easy once you understand.

Models/Customer & Address: This is our complex clone model. Every customer has at least one address. So Customer model contains an address model.

ShallowCopy(): It is used for coping with all primitive types of properties to a new model. We will use, MemeberwiseClone() method of the System namespace.

And one more thing. If you want to Clone a model in C#, you have to mark by the [Serializable] attribute.

Let’s check the belowe codes.

  • Firstly we create, Customer Ali and set all his properties. Include HomeAddress.
  • Secondly, we create Customer Veli by cloning Ali.
  • And finally, we change some properties of Veli. “FirstName” and “HomeAddress” properties.

What is the result of all “Console.WriteLine()” methods ? :)

We change some of Veli’s property and write Console to Ali’s property. As you see in the below picture, Ali’s name, not change but address properties changed and become as same as Veli’s address. But why?

In my carrier life, maybe more than one hundred time juniors asked me this question :)

Because FirstName, LastName and Id is Primitive type, but HomeAddress is a class or rather an object. FirstName and LastName are strings. They keep in Heap memory, but they are immutable. When we try to change, the compiler creates a new string object in the Heap memory. So Veli’s Name and Surname changing is not affected to Ali. But Address is a class. So it keeps in Heap memory, and the pointer address is kept in Stack memory.

When we called the “MemberwiseClone()” method for a Customer, we copy only the pointer address of HomeAddress class from Stack Memory. But the values of the Address is the same because Ali’s and Veli’s HomeAddress pointer show the same Address Object value. So when we changed Veli’s HomeAddress, Ali’s HomeAddress is changed too.

So we need more detailed something. We have to clone the object value of the Customer’s property HomeAddress too. We need Deep Copy.

In the below code, we removed the “ShallowCopy()” and added the “DeepCopy()” method.

“People change. Memories don’t.”

Let’s try the same thing with Deep Copy:

As you see in the below picture, Ali’s name, surname, and address’s properties are not changed. So we succeed to clone not only primitive type but also Complex Type properties(HomeAddress) from Ali to Veli by using Deep Copy.

Conclusion:

In this article, we tried to understand Memory Structure and how can we manage it. Casting is so important. We have to very careful when we assigned Bigger types to Small types. And also, a shallow copy does not always save the day for Complex Types.

I hope this article helps you to understand what is Heap and Stack memory. And gives an idea about the Deep and Shallow Copy difference.

“If you have read so far, first of all, thank you for your patience and support. I welcome all of you to my blog for more!”

Source Code: https://github.com/borakasmer/Deep-Shallow-Copy

Source:

--

--

Bora Kaşmer

I have been coding since 1993. I am computer and civil engineer. Microsoft MVP. Software Architect(Cyber Security). https://www.linkedin.com/in/borakasmer/