1

Recently, I found a post on Internet asking this question, I tried to figure out but not sure.

I guess the problem is related with Boxing and Unboxing:

public readonly object counter = new Counter();

This line boxs a Counter onto heap, and counter refers to it.

((Counter)riddle.counter) 

This line unboxs the Counter from heap.

Every time data it unboxs from heap is same as origin. Therefore, Line A doesn't affect Line B because they both retrieve from heap and are two different instances of Counter.

Is that right? Sorry for my poor English.

public void WantToKnow()
{
    var riddle = new Riddle();
    ((Counter)riddle.counter).Increment();  // Line A

    Console.WriteLine(((Counter)riddle.counter).Count); // Line B
    // Why the output is 0?///////////////////
}

struct Counter
{
    private int x;
    public void Increment() { this.x++; }
    public int Count { get { return this.x; } }
}

class Riddle
{
    public readonly object counter = new Counter();
}
Netsphere Wu
  • 45
  • 1
  • 7
  • 1
    Possible duplicate of [Why are mutable structs “evil”?](https://stackoverflow.com/questions/441309/why-are-mutable-structs-evil) – adjan May 07 '18 at 05:54
  • Did you try to debug step-by-step? – Zohar Peled May 07 '18 at 06:00
  • 1
    Remove the readonly from counter variable inside the Riddle class. – Raviraj Palvankar May 07 '18 at 06:04
  • 1
    @RavirajPalvankar: That wouldn't change anything in itself. The OP *could* rebox the modified counter and assign it back to the field, but that's different. – Jon Skeet May 07 '18 at 06:49
  • @DaisyShipton Remove the readonly and also change the datatype from object to Counter and remove all the type casting done inside WantToKnow(), it will increment the counter. The typecasting is treating it as a new object. – Raviraj Palvankar May 07 '18 at 06:53
  • @RavirajPalvankar: Well yes, if you *completely* change the code, the results will change - but the OP is asking about why it behaves the way it does now... and your original comment didn't say anything about the rest of the changes. – Jon Skeet May 07 '18 at 07:49

2 Answers2

1

Firstly, this is all a good example of why I try to avoid mutable structs in almost all cases. While you can usually predict what will happen if you pay enough attention, it's easy to miss one copy along the way which messes everything up. If you keep all structs immutable, life is simpler.

For your question: yes, you're unboxing which creates a copy of the counter. Changing that copy doesn't affect anything else. (There's IL to unbox without copying, but C# never uses that.)

You can make this work by making your counter implement an interface with an Increment operation. At that point you can cast to the interface instead of the value type, which means it's not unboxing. Your Increment operation would then modify the value within the box, which means you can then get at it again. Here's a complete example:

using System;

class Program
{
    static void Main()
    {
        var riddle = new Riddle();
        ((ICounter)riddle.counter).Increment();

        Console.WriteLine(((Counter)riddle.counter).Count); // Line B
    }
}

interface ICounter
{
    void Increment();
}

struct Counter : ICounter
{
    private int x;
    public void Increment() { this.x++; }
    public int Count { get { return this.x; } }
}

class Riddle
{
    public readonly object counter = new Counter();
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
0

Because struct is value type- You can use "class Counter instead "struct Counter" or use below solution
Reason : ((Counter)riddle.counter).Count is treat as another copy of struct not as ref type. so it's showing initial value 0

using System;
using System.Text.RegularExpressions;

public class Program
{
    public static void Main()
    {

         var riddle = new Riddle();
             Console.WriteLine(((Counter)riddle.counter).Increment());
            ///////////////////////////////////////////
            Console.WriteLine(((Counter)riddle.counter).Count);
            // Why the output is 0?///////////////////
    }


}
struct Counter
        {
            private int x;

            //Change is here    
            public int Increment() { this.x++; return this.x; }
            public int Count { get { return this.x; } }
        }

        class Riddle
        {
            public readonly object counter = new Counter();
        }
Siva Rm K
  • 284
  • 1
  • 6