Value passing and reference passing of value type and reference type in Csharp


In order to consolidate the basic knowledge in Csharp and lay the foundation for the upcoming interview, I found a strange thing, that is, I expressed all kinds of incomprehension and confusion about the value type and reference type, as well as the definition and performance of various transfers of value type and reference type, As a quasi software development engineer who wants to use C# as the language for software development, it is very necessary.

Heap and stack

To understand what is a value type and what is a reference type, I first thought that this thing is to use something similar to an article. I don't quote more nouns and definitions to avoid making myself look confused when I look back. But first of all, since the value type and reference type are "things" or "items" to be delivered, express delivery in real life, All need a warehouse or even a carriage to store express mail, so the concept to be introduced here is a place for storing "items" of this value type and reference type - heap and stack

heap

It's literally easy to understand. It's a blank place. Just build things up. But readers with a slightly better language or a slightly more accurate sense of language may find (I don't say they despise people with a bad sense of language, ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha ha. For example - a pile of disorderly books, etc.

What's the pile for?

To Liao! In this C #, the heap is used to store instance objects in C #. Of course, who has nothing to do with writing a project? All the "things" passed in a day are instance objects? Of course, you should also store data! And a lot of data! (don't ask me how much this is, emmmm)

What are the characteristics of the pile?

When I was sorting out the materials, I found that this was the most common question I asked myself.
Considering from its Chinese name, heap is used to describe disorderly (if it is orderly, I prefer to use group) if you think so, it is right for Liao! This is the first feature.

  1. Stored data or instance objects are out of order.
  2. Since there is no order, of course you can access the data or instance objects you want to save at will!
  3. There is also the dynamic allocation of storage space.

So what's wrong with the pile?

Yes, yes, it is slow in reading speed and can't automatically recycle expired instance objects (of course. net has GC, but C + + players are a little miserable...).
Because there is no order, you are sending a request to the system. I want to find some data! The system will find what you want from the pile, just like you find your favorite comic book in the pile. So the time will be slower.

Stack

After talking about pile, let's talk about the term stack.

What is stack like relative to heap?

In short, you can think of the stack as a "happy potato chip bucket".

So what are the characteristics of stack?

Stack is very intelligent compared with heap.
When you pick up a "happy potato chip bucket" with a bottom at the back, you need to put potato chips one by one, pick it up and put it in, right? If the first piece of "potato chips" you put in is coke flavor, the second piece you put in is lime cucumber flavor, and the third piece is barbecue flavor, and you suddenly think that you haven't eaten coke flavor potato chips yet, at this time, you want to take it out. So you have to take out the top two potato chips first, and then take out the bottom potato chips.
The characteristic is!

  • Stack can only be operated at one end! That is what we call "the mouth of the potato chip bucket", but we generally call it the top of the stack. The storage method is like the potato chip storage method of "Leshi potato chip bucket", following the principle of "first in, last out, first in".
  • Stack is a memory self-management structure. Data is automatically allocated into the stack, and the memory occupied is automatically emptied out of the stack.
  • Although it seems that the process of accessing data in a stack is very troublesome, but! Its performance efficiency in multi data is faster than that of heap storage.

Is there anything wrong with storing data in a stack?

Yes! Everything has two sides!

  • The memory in the stack cannot be requested dynamically, that is, it can only be allocated according to the data size, and the flexibility is not as high as the heap.
  • Just like the chips in the "Leshi potato bucket", only a certain amount of data can be stored, so we should consider the impact of data size when using it.

Allocation of value type and reference type in stack and heap

There are actually two rules here!

  • When creating a reference type, memory management will allocate a piece of space in the heap, and then allocate a piece of memory to the stack to store the memory address in the heap! (yes, it's the pointer...)

  • When creating value type data, memory management will allocate a piece of memory for it, and then this memory is placed where the value type data is created.
    To be honest, I didn't understand this sentence at first, especially where it was created, but I thought about it. It can be divided into the following two cases:

  • If the value type data is created inside the method, its memory address will be put on the stack along with the method.

  • If the value type data is a member variable of the reference type, its memory address will be stored in the heap following the reference type.

Value type

What is a value type? I don't have any definitions, but the collected data clearly points out that byte, short, int, long, float, double, decimal, char, bool and struct are collectively referred to as value types.

reference type

The reference types in C # are: class and string

The essential difference between value type and reference type:

  1. The value type is allocated on the thread stack (management is the responsibility of the operating system), and the reference type is allocated on the managed heap (management is the responsibility of the garbage collector GC). Management here refers to the allocation and recycling of memory.
  2. The value type inherits from ValueType, which inherits from system Object; The reference type inherits directly from system Object.
  3. When the value type ends in the scope, it will be automatically released by the operating system to reduce the pressure of the managed heap; The reference type depends on GC. Therefore, the value type has advantages in performance.

pass by value

Pass it by value!

Value passing of value type

Say nothing! Directly on the code!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;

namespace Test3
{
    class Program
    {
        static int pass(int PassNum, int PassNum2)
        {
            int MaxNumber = PassNum + PassNum2;
            return MaxNumber;
        }
        static void Main(string[] args)
        {
            int Maxnumber1 = 6;
            int Maxnumber2 = 7;
            int ShowMax = pass(Maxnumber1, Maxnumber2);
            WriteLine(ShowMax);
            ReadLine();
        }
    }
}

The operation result is:
You can see that the running result is 13.
The parameter transfer is the value type transfer. The value type copies the data itself to form an independent data block.

Value passing of reference type:

Continue with the code!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Test4
{
    class Program
    {

        static void ChangeArray(int[] CArray)
        {
            CArray[0] = 888;
            CArray = new int[5] { -3, -2, -1, -3, -4 };
            WriteLine("In this method, the first element is,{0}", CArray[0]);

        }

        static void Main(string[] args)
        {
            int[] CArray = { 1, 2, 3, 4, 5 };
            WriteLine("stay Main Inside, ChangeArray Before the method call, the first element of the array is,{0}", CArray[0]);

            ChangeArray(CArray);
            WriteLine("stay Main Inside, ChangeArray After the method is called, the first element of the array is,{0}", CArray[0]);
            ReadLine();
        
        }
    }
}

In fact, when I was writing this example, I was fascinated by how the array in the parameter is {888,2,3,4,5} after calling the ChangeArray function. In my imagination, it should be {888, - 2, - 1, - 3, - 4}:
Similarly, at the beginning of the program, the Main method gives the following values:
Address after passing reference type value to method:
An array of new changes the array pointer in the passed in method.

After calling the method, the array with the same address changes the first value, so the first value is 888 As can be seen from the address pointer of the array, the array of CArray after calling the method is actually the one declared by the Main at the beginning, not the one assigned by new in the method.
Operation results:
It can be seen from this that when the value passes reference type data, although it is copied, it will create a new address. In fact, an array will be changed into two addresses, and different values will be stored respectively for calling.

Reference passing

Reference passing of value type:

Code!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;

namespace Test3
{
    class Program
    {
        static int pass(ref int PassNum,ref int PassNum2)
        {
            int MaxNumber = PassNum + PassNum2;
            return MaxNumber;
        }
        static void Main(string[] args)
        {
            int Maxnumber1 = 6;
            int Maxnumber2 = 7;
            int ShowMax = pass(ref Maxnumber1, ref Maxnumber2);
            WriteLine(ShowMax);
            ReadLine();
        }
    }
}

What is the difference from the above? It's ref!
With this "prefix", you should understand:
In this example, Maxnumber1 and Maxnumber2 are not passed, but their references. The parameters PassNum and PassNum2 in the method are not of type int, but are references to type int.

Reference passing of reference type:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Test4
{
    class Program
    {

        static void ChangeArray(ref int[] CArray)
        {
            CArray[0] = 888;
            CArray = new int[5] { -3, -2, -1, -3, -4 };
            WriteLine("In this method, the first element is,{0}", CArray[0]);

        }

        static void Main(string[] args)
        {
            int[] CArray = { 1, 2, 3, 4, 5 };
            WriteLine("stay Main Inside, ChangeArray Before the method call, the first element of the array is,{0}", CArray[0]);

            ChangeArray( ref CArray);
            WriteLine("stay Main Inside, ChangeArray After the method is called, the first element of the array is,{0}", CArray[0]);
            ReadLine();
        
        }
    }
}

There's no mystery. I changed the address directly.
The reference passes the main method of the reference type instance to the value:
After entering the method, the array address and array value are changed due to new:
Until the end of the run, the address did not change.
The operation results are as follows:
It can be seen that after passing the reference parameters, the referenced method does not copy the array and adds an array address pointer, but changes the value in the original new array, so the output is - 3 instead of 888.

Pass return instead of reference:

In fact, this is the homework left by the teacher for me!
Code!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace Test4
{
    class Program
    {

        static int[] ChangeArray( int[] CArray)
        {
            CArray[0] = 888;
            CArray = new int[5] { -3, -2, -1, -3, -4 };
            WriteLine("In this method, the first element is,{0}", CArray[0]);
            return CArray;
        }

        static void Main(string[] args)
        {
            int[] CArray = { 1, 2, 3, 4, 5 };
            WriteLine("stay Main Inside, ChangeArray Before the method call, the first element of the array is,{0}", CArray[0]);

             CArray = ChangeArray(CArray);
            WriteLine("stay Main Inside, ChangeArray After the method is called, the first element of the array is,{0}", CArray[0]);
            ReadLine();
        
        }
    }
}

It's originally a value transfer, but we don't want to get the effect of reference transfer: we can send out the array in the method with return and create a variable in the mian method to accept it. Of course, the variable name can be different to indicate that the outgoing array is a copied array after value transfer.
The operation results are as follows:

That's it! Thank you!

Keywords: C#

Added by staffanolin on Sat, 19 Feb 2022 21:37:53 +0200