Thoughts from a simple interview question

A few days ago, a WeChat friend suddenly sent me a screenshot and opened it to see a piece of code. The title is as follows, asking the number of packing and unpacking and the final output .

namespace DotNetCore
{
    class Program
    {
        static void Main(string[] args)
        {
            var p = new Point(1,1);
            Console.WriteLine(p);
            p.Change(2,2);
            Console.WriteLine(p);
            object o = p;
            ((Point)o).Change(3,3);
            Console.WriteLine(o);    
        }       
    }

    struct Point
    {
        private int a,b;
        public Point(int a,int b)
        {
            this.a = a;
           this.b = b;
        }

        public void Change(int a,int b)
        {
            this.a = a;
           this.b = b;
        }

        public override string ToString()
        {
            return $"({a},{b})";
        }
    }
}

It's very simple to look at, isn't it just test value types and reference types? so easy, throw a hand and an answer:
packing 1 time, unpacking 1 time,
return result:
(1,1)
(2,2)
(2,2)

Facts have proved that this answer can only be regarded as half of the answer, the question is deep! ! !

Illustration of the execution process

The most intuitive, simplest and most effective is of course to go directly to IL. What is IL? IL is the intermediate language after the code is compiled (Intermediate Language) . The IL of the Main method code above is shown in the figure below.

I believe that an execution process of the code that has been described in detail in the above figure, even people who are not familiar with IL should be able to understand the execution process of the entire code. I think there are two main pitfalls in this topic:

  • ((Point)o).Change(3,3); Some people may think that the output is (3,3). Through the two lines of IL_0044 and IL_0045 in the above figure, we can clearly know that ((Point)o) The variables after the box are not used later. The output is still the value of the o variable itself;
  • Console.WriteLine(); statement, this method does not have an overload of struct type, so it calls the method that uses Object type parameters, so every output needs to be boxed, and we can also see it clearly through IL .

Therefore, the correct answer to this question is as follows:
box 3 times, unbox 1 time, and
return the result:
(1,1)
(2,2)
(2,2)

some thinking

The boxing and unboxing of value types will bring performance loss, because data needs to constantly move back and forth on the thread stack (stack) and the managed heap (manager Heap), and sometimes some methods or types we use inadvertently, just Implicit boxing is included, for example:
Hashtable ht = new Hashtable();
ht.Add(a,b);

When a and b are value types or reference types, respectively, the execution efficiency is different, and it can be imagined if it is a large-scale data processing problem.
Technology can't be sloppy. So, I think whether you write your own programs or use some third-party APIs, you should have a deeper understanding of some internal implementations. Although it is easy to use them, you may fall into the pit one day. Any questions are welcome to discuss.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325112964&siteId=291194637