7

I came along an (according to me) strange difference between structs and interfaces in C#. Consider this interface and struct:

public interface INumber
{
    void ChangeNumber(int n);
    void Log();
}
public struct Number : INumber
{
    private int n;
    public void ChangeNumber(int n)
    {
        this.n = n;
    }
    public void Log()
    {
        Console.WriteLine(this.n);
    }
}

When I create a new class with a Number as property, use the ChangeNumber method to change n to 2 and print the number by using Log, it prints 0 instead:

public class NumberContainer
{
    public Number Number { get; set; }
    public NumberContainer()
    {
        this.Number = new Number();
        this.Number.ChangeNumber(2);
        this.Number.Log();              //prints 0...
    }
}

After a while I realised it was because when I call this.Number.ChangeNumber(2);, I actually create a new object (because of the getter) and change that number to 2. But then I changed a little bit of the code by changing the Number property to an INumber property:

public class NumberContainer
{
    public INumber Number { get; set; }
    public NumberContainer()
    {
        this.Number = new Number();
        this.Number.ChangeNumber(2);
        this.Number.Log();              //prints 2!
    }
}

In this case, it prints 2! Why is this happening? Doesn't the same principal of structs apply to the interface?

Safron
  • 802
  • 1
  • 11
  • 23
  • 1
    When it's referenced as the interface it gets boxed. But in general it's best to make structs immuteable or if you need muteability you should make it a class. – juharr Jan 20 '16 at 17:48

2 Answers2

4

The difference is that struct is used as a value type, where interface (which can be implemented by a class or a struct) is a reference type.

That makes a huge difference in your example. What you are doing in the first case the call to this.Number means "Get me the value of the number" - which means it pulls the value on stack, and the (unnamed) variable on stack, which is not stored anywhere, gets modified.

In the other case, the interface is a reference type - which means, it gets whatever is stored on its address and modifies it.

Generally I would not suggest having a mutable struct (as already mentioned in comments).

You can read more on this topic e.g. here: Why are mutable structs “evil”?

Community
  • 1
  • 1
Tomas Pastircak
  • 2,867
  • 16
  • 28
2

This is caused by auto property in NumberContainer class, you always get a copy of value when you are accessing property.

If you change property to a field, it works as expected. Remember that autoproperty is just pair of methods, and that value types are copied when returned/passed to/from any method.

When you are calling

        this.Number.ChangeNumber(2);
        this.Number.Log();              //prints 0...

you are actualy calling:

 this.getNumber() // returns copy of value type
        .ChangeNumber(2); // executes op on that copy

    this.getNumber()
        .Log();

When you are using interface you are returning reference to an object, so operations are always performed on the same object.

csharpfolk
  • 4,124
  • 25
  • 31