Struct v/s Class in C# - Please explain the behavi

2019-07-18 04:50发布

问题:

Could someone please explain the behavior of this

  class testCompile
    {
       /*
        *   Sample Code For Purpose of Illustration
        */
       struct person 
       {
           public int age;
           public string name;

       }

        static void Main(string[] args)
        {
            List<person> Listperson = new List<person>();
            person myperson = new person();

            for (int i = 1; i <= 2; i++)
            { 
                //Assignment
                myperson.age = 22+i;
                myperson.name = "Person - " + i.ToString();
                Listperson.Add(myperson);
            }
            int x = 0;
            while (x < Listperson.Count)
            {
                //Output values
                Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
                x++;
            }
        }
    }

/*  
    Output:
    Person - 1 - 23
    Person - 2 - 24
*/

Why am I not getting the same output for a class as that of a struct?

class testCompile
    {
       /*
        *   Sample Code For Purpose of Illustration
        */
       class person 
       {
           public int age;
           public string name;

       }

        static void Main(string[] args)
        {
            List<person> Listperson = new List<person>();
            person myperson = new person();

            for (int i = 1; i <= 2; i++)
            { 
                //Assignment
                myperson.age = 22+i;
                myperson.name = "Person - " + i.ToString();
                Listperson.Add(myperson);
            }
            int x = 0;
            while (x < Listperson.Count)
            {
                //Output values
                Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
                x++;
            }
        }
    }
/*  
    Output:
    Person - 2 - 24
    Person - 2 - 24 
*/

回答1:

Classes are reference types, structs are value types.

When a value type is passed to a method as a parameter, a copy of it will be passed through. That means that you add two completely separate copies of the Person struct, one for each pass in the loop.

When a reference type is passed to a method as a parameter, the reference will be passed through. That mean that you add two copies of the reference to the same memory location (to the same Person object) - when making changes to this one object, you see it reflected in both references since they both reference the same object.



回答2:

It's the difference between value type (struct) and reference type (class).

  • When you're adding the struct to Listperson the content of person is put in the list, you have two different person struct in your list.

    for (int i = 1; i <= 2; i++)
    { 
      //Assignment
      myperson.age = 22+i;
      myperson.name = "Person - " + i.ToString();
      Listperson.Add(myperson);
      /* First time: 
         Listperson contains a person struct with value { age = 23, name = 1}
         Second iteration:
         Listperson contains a person struct with value { age = 23, name = 1}
         Listperson contains another person struct with value { age = 24, name = 2} 
      */
    }
    
  • When you're adding the class the reference is put in the list, you have two references that referenced the same person object.

    for (int i = 1; i <= 2; i++)
    { 
      //Assignment
      myperson.age = 22+i;
      myperson.name = "Person - " + i.ToString();
      Listperson.Add(myperson);
      /* First time: 
         Listperson contains 1 reference to myperson object with value { age = 23, name = 1}
         Second iteration:
         Listperson contains 2 reference to myperson object with value { age = 24, name = 2} 
      */
    }
    


回答3:

Because your myperson variable only ever deals with one person struct/class.

What you add to the list, in your loop, is a copy of your myperson variable - which for the struct, will be an entire copy of the struct, but for the class will be a copy of the reference to the single instance that you create (and mutate).



回答4:

If you want same result then bring person declaration inside of the for loop:-

           // person myperson = new person();
           //Move the upper line inside the for loop
            for (int i = 1; i <= 2; i++)
            { 
               person myperson = new person();
                //Assignment
                myperson.age = 22+i;
                myperson.name = "Person - " + i.ToString();
                Listperson.Add(myperson);
            }

In struct you adding a value type hence separate values are stored, whereas in class you are adding reference to the object hence gettng same value.



回答5:

In the second instance, you're addding a reference type. In fact, you're adding the same item, twice, since your

  = new person()

is not in the loop. So it always points to the same object you initialized here:

 person myperson = new person();

Even after it's added to your list, the changes affect it.

In the first instance, you're adding a struct each time, which is a value type, so will be copied into the list. Changes you make after that no longer refer to the object in the list, so they have different values.



回答6:

Structures are value types and classes are reference types. So in your first example when you add myperson to the list your adding a copy of myperson and the myperson variable still refers to a separate copy. In you second example myperson is a reference type so your adding two pointers to the same object.



回答7:

You should understand key distinctions between structs (Value Types) and classes (Reference Type). You could easily find this information in Google or at SO.

When you add struct instance to List you create another separate copy for this instance, and when you change one element you did not change another.

But in case of classes you create one instance and uses this one "shared" instance with two references (list[0] and list1) and you could change this one instance through two different references, that's why when you change list[0] item it seems that you change list1 item too.

Consider following code:

var s1 = new SampleStruct { X = 1, Y = 1 };
var s2 = s1; 
//Creating separate copy
//Lets check this
Console.WriteLine(object.ReferenceEquals(s1, s2)); //Prints False

var c1 = new SampleClass { X = 1, Y = 2 };
var c2 = c1;
//We do not create any copy
// two references c1 and c2 "pointed" to one shared object
Console.WriteLine(object.ReferenceEquals(c1, c2)); //Prints True

Similar behavior we have when we pass parameter to function (or adding element to list).



回答8:

In the second example you're only creating on item and adding a reference to to the list many times.



回答9:

When you add the struct to the collection, it makes a copy of it. It's a value type. You'll end up with two distinct objects in the collection, each with different values. This is probably the expected behavior.

When you add the class, the reference type, to the collection, a new object is not created. You're actually adding two different references to the same object. You'll end up with (apparently) two objects with the same value. It's actually the same object, seemingly appearing twice in the collection.



回答10:

Think of variables and parameters of class types as holding an "instance IDs". The only things one can actually do directly with an instance ID are (1) create a new one (which will be assigned to a new instance of a class), (2) assign one to another, or (3) check two IDs to see if they are equal. Doing anything else with a variable, parameter, etc. of a class type is a short hand for "do _ to the instance referred to this instance ID".

So code like:

{
  Car A,B,C; /* Car is a class */
  A = new Car;
  B = new Car;
  C = A;
  A.color = carColors.Yellow;
  B.color = C.color;
}

The first "new" statement will create an instance of Car and put its instance ID (let's say #1234) in "A". The second will create another car instance (#4321) and store its ID in B. The next statement will copy #1234 into C. It doesn't do anything with the car--it just copies the ID. Then car #1234 will be painted yellow, then in the last statement, the color of car #1234 (i.e. yellow) will be used to paint car #4321. Note that while A and C are different variables, they both hold the same instance ID (#1234) and thus refer to the same car.