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();
}
6
  • 1
    Possible duplicate of Why are mutable structs “evil”? Commented May 7, 2018 at 5:54
  • Did you try to debug step-by-step? Commented May 7, 2018 at 6:00
  • 1
    Remove the readonly from counter variable inside the Riddle class. Commented May 7, 2018 at 6: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. Commented May 7, 2018 at 6: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. Commented May 7, 2018 at 6:53

2 Answers 2

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();
}
Sign up to request clarification or add additional context in comments.

Comments

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();
        }

3 Comments

That wouldn't do very much good though - try copying your line of code that calls Increment several times, and you'll see that it's still not actually implementing the counter in Riddle. The final output is still 0.
Yes.I got that.
So how is this a "solution"?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.