Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# getters/setters in structs and interfaces

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?

like image 201
Safron Avatar asked Jan 20 '16 17:01

Safron


2 Answers

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”?

like image 191
Tomas Pastircak Avatar answered Oct 06 '22 00:10

Tomas Pastircak


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.

like image 45
csharpfolk Avatar answered Oct 05 '22 23:10

csharpfolk