Depth analysis of differences between C median and reference types

  • 2020-12-10 00:48:51
  • OfStack

This article has taken a straightforward look at the differences between C# median and reference types. Share to everybody for everybody reference. The specific analysis is as follows:

It seems that the "difference between value type and reference type" is a popular trend in this year's interview. I have been interviewed three times in a row (so far there are only three in total). What is the probability of this happening on the first question?

To get back to the point, let's discuss what the difference is between these two. I remember once telling the interviewer directly in a phone interview, "The value type is cash, and the reference type is passbook." Later, I think I blurted this out a little impulsively, but it was ok. I am not good at reciting theoretical dogmas. I like to understand and remember those rigid words in books in connection with common things in real life.

To put it bluntly: value types are cash, to be used directly; The reference type is a passbook, and you have to withdraw money from the bank to use it.

Declare a value type variable, and the compiler allocates a space on the stack that corresponds to the value type variable, in which the value is stored. An instance of a reference type is allocated on the heap. Create a new instance of the reference type and the variable value will correspond to the memory allocation address of the instance, just like your bank account 1. You can just go through the book and memorize 1, but I think after a period of development, even if you can't recite the definitions from the book, you won't confuse value types with reference types. Next, as usual, let's talk according to the code.

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
 
public static class ReferenceAndValue
{
    public static void Demonstration()
    {
        Person zerocool = new Person { Name = "ZeroCool", Age = 25 };
        Person anders = new Person { Name = "Anders", Age = 47 };
 
        int age = zerocool.Age;
        zerocool.Age = 22;
 
        Person guru = anders;
        anders.Name = "Anders  Hejlsberg";
 
        Console.WriteLine("zerocool's age:\t{0}", zerocool.Age);
        Console.WriteLine("age's value:\t{0}", age);
        Console.WriteLine("anders' name:\t{0}", anders.Name);
        Console.WriteLine("guru' name:\t{0}", guru.Name);
    }
}

In this code, we first create an Person class, which contains both Name and Age attributes. Needless to say, the Person class is a reference type, so is Name, because it is of string type (but string is a special reference type, which will be discussed in a future article), but Age is a value type. Next, let's look at the Demonstration method, which demonstrates the difference between value types and reference types.

First, we declare two instance objects of the Person class, zerocool and anders. As mentioned earlier, both objects are allocated on the heap, and zerocool and anders themselves are really just references to the starting address of the memory region where the object is located -- in other words, Pointers to it. We also initialized the object instance separately when we declared it. First, let's look at the value type member of the zerocool object, which we assign to 25 (yes, I'm 25 years old), and the Name property of anders (you'll see who it is in a moment), which we assign to "Anders". Let's get to work. Let's see how we do it.

We declare a value type variable, age, and assign the value of zerocool's Age to it directly on initialization. Obviously, the value of age is 25. But at this time zerocool was not happy, he wanted act young, secretly changed his age to 22, just enough for the legal age of marriage. Then we declare an guy object of reference type, assign anders to it when initialized, and anders reveals its true name as "Anders Hejlsberg" (in honor of the father of C#). Now let's agree to each of these variables and see what the difference is.

Result01

You may wonder (or you may wonder, then, why did we change the value of zerocool. Age, not age, but anders. Name, and guru. Name? This is the difference between a value type and a reference type. We declare the age value type variable and assign zerocool.Age to it. The compiler allots 1 space on the stack and fills in the value of ES61en.Age. That's all. And reference type 1 sample, we at the time of statement guy anders assigned to it, said earlier, a reference type is just the heap data area address references, is actually the guy anders reference is also given, so this 2 went from point to area with 1 piece of memory, since it is pointing with 1 area, so don't tube who moved inside of "cheese", the other one sold out will follow the result of the change, like credit card 1 sample with calling CARDS, took the money with calling CARDS, associated with the credit card account change will follow. Mention money, estimate big guy son impression deep some, ha ha!

In addition, there will be a performance difference. Since one is directly operating memory and the other one takes one more step to resolve the reference address, it is clear that many times the value type will reduce the system performance overhead. However, "most of the time" does not mean "all of the time", and sometimes you need to act accordingly. For example, when you need to pass or return a lot of function parameters, you should always copy fields in this way, which will actually reduce the performance of the application. In addition, if instances are frequently used in collections such as Hashtable or ArrayList, these classes encase value type variables within them, which also results in additional memory allocation and memory copy operations, which are not cost-effective from an application performance perspective.

Oh yes, there is one concept mentioned above, packing. So what is boxing? Boxing is the process of converting a value type to a reference type. Boxing a value type variable into a reference type variable first allocates memory space for the new reference type variable on the managed heap, then copies the value type variable into the newly allocated object memory on the managed heap, and finally returns the newly allocated object memory address. Packing is reversible, so there is unpacking. The unboxing operation gets a pointer to the part of the object that contains the value type, and the programmer manually copies its corresponding value to the value type variable. Let's look at typical packing and unpacking operations.

public static class BoxingAndUnboxing
{
    public static void Demonstration()
    {
        int ageInt = new int();
 
        // Boxing operation.
        object ageObject = ageInt;
 
        //ageObject = null;
 
        // Unboxing operation.
        ageInt = (int)ageObject;
 
        Console.WriteLine(ageInt);
    }
}

In this method, we first declare a value type variable, ageInt, but do not assign it a value, then declare a typical reference type variable, ageObject, and assign ageInt to it, and we do a boxing operation here. The compiler now allocates a block of memory space on the managed heap (the size of the total space occupied by the value-type variables contained in the object plus 1 method table pointer and 1 SyncBlockIndex), copies ageInt into this space, and returns the reference address of that space. The next line, line 13, is an unboxing operation in which the compiler gets a pointer to the value type variable in the ageObject object and copies its value to the value type variable. If you open the commented out code on line 10 (informally, uncommented), then line 13 will throw the ES85en.NullReferenceException exception, which, if asked, will involve another big difference between the value type and the reference type. As you can see, ageInt was declared without an assignment. If you close line 10, the program does not report an error, and finally prints out a 0. This means that when declaring a value type variable, if no assignment is initialized, the compiler automatically assigns it to 0, and since the value type is not referenced, it cannot be null. The reference type is not the same, it can be a null reference, an expired bank card can exist. If you unbox an empty object, where does the compiler find a pointer to a value type variable in it? So that's something to be careful about with unpacking.

Finally, we've outlined a few other obvious differences between value types and reference types so that you can get through Question 1.

All value types inherit from ES92en.ValueType, but ValueType does not attach any methods other than System.Object, although it does rewrite Equals and GetHashCode. The Equals method of the reference type variable compares the reference address of the 2 instead of the internal value, and the Equals method of the value type variable compares the value of the 2 instead of... Oh, and the value type doesn't have a reference address at all;
A value type cannot be used as a base type for any other type, so no new virtual methods can be added to the value type, let alone any abstract methods, all of which are sealed (non-overridden);
Unboxed value types are allocated on the stack rather than on the heap, and the stack is not the domain of GC, so GC does not care about the value type variable. Once the scope of a value type variable is over, the memory occupied by GC is immediately reclaimed, without GC doing it himself.
All of the above are the main differences between value types and reference types, both in text and code. I believe it should leave you a deep impression, although not deep enough, I hope it can play a role in the introduction. If I go to interview for the SDE position, I think this depth will be about right.

Hopefully this article has helped you with your C# programming.


Related articles: