C#: structs and Interface


The fact that a struct can implement an interface is well known and so is the fact that casting a value type into an interface leads to boxing of the value type. This is because methods in interfaces are defined as virtual and to resolve virtual references, vtable (method table) look up is required. Since value types do not have pointers to vtable they are first boxed into a reference type and then the look up happens.


This boxing leads to some performance penalty. See Rico Mariani’s performance quiz for an example.


The fact that such boxing takes place on casting to interfaces can lead to subtle issues in code. Consider the following

interface IPromotion {

void promote();

}

struct Employee : IPromotion {

public string Name;

public int JobGrade;

public void promote() {

JobGrade++;

}

public Employee(string name, int jobGrade) {

this.Name = name;

this.JobGrade = jobGrade;

}

public override string ToString() {

return string.Format(“{0} ({1})”, Name, JobGrade);

}

}

class Program

{

static void Main(string[] args)

{

Employee employee = new Employee(“Cool Guy”, 65);

IPromotion p = employee;

Console.WriteLine(employee);

p.promote();

Console.WriteLine(employee);

}

}


Here the output would be
Cool Guy (65)
Cool Guy (65)

So even after calling p.promote() the value of JobGrade in employee does not increase. The reason is that on implicitly casting employee to IPromotion p a temporary boxed object is created and p.promote updates the JobGrade in this temporary object and not on original employee value type. Since after this usage the temporary boxed object is not refered to, the update to it is lost and it is garbage collected.


If however we change the code and make Employee a class the output would become as expected


Cool Guy (65)
Cool Guy (66)


The reason being that now the boxing does not take place and the update happens on the original employee object.

Comments (20)

  1. Rick Scott says:

    That does make perfect sense, but only after some thought. A struct implementing an interface is, to say the least, something one should approach with great caution. Perhaps a compiler warning is appropriate?

  2. Keith Farmer says:

    I recall reading somewhere that structs should not be used for mutable types. You could consider them as points in a space consisting of many values.

    promote(), really, is as much a mutation as String.Trim(), and should return a different point in the employee space: one that represents the employee after promotion.

  3. James Arendt says:

    As kfarmer stated, the example violates the design that structs should be immutable value-types. Yes, boxing is going on here, but the code would still have the same issue if you took out boxing and simply assigned the employee object to another variable.

    Employee employee = new Employee("Cool Guy", 65);

    Employee employee2 = employee;

    Console.WriteLine(employee2);

    employee.promote();

    Console.WriteLine(employee2);

    Structs are value-types. They should be treated as such. That’s the more important lesson to take from this example.

  4. Andrei Ignat says:

    With lots a feature – the language becomes more and more difficult to understand – and for NO GOOD REASON.

    It was REALLY necessary that a struct can implement an interface?

  5. James Arendt says:

    Andrei, I would argue that the problem is not that a struct can implement an interface, but that the language designers do not enforce immutability for structs. Allowing structs to implement interfaces simplifies things by allowing developers to treat all types in their program more uniformly.

  6. Raj says:

    One important factor to consider is time.

    To allocate an array of 1000 structs is a lot quicker (as one doesnt have to go through initializing each and every struct object). Where as if one had to allocate a 1000 class objects – that would take a lot more time.

  7. Jeroen says:

    As James correctly states, the problem would be the same if you used two instances of the struct. The problem is actually just the opposite: the struct will behave like a class if you cast it to the interface:

    IPromotion employee = new Employee("Cool Guy", 65);

    IPromotion employee2 = employee;

    Console.WriteLine(employee2); // outputs 65

    employee.promote();

    Console.WriteLine(employee2); // outputs 66!!

    The copy from employee to employee2 now only copies the *reference* to the (already boxed) instance, so both references reference the *same* value.

  8. Great post, Abhinaba.  

    And thanks, Jeroen, for suggesting the workaround.  It actually works in my case to rearrange the lines a bit as you have to get around my problem.

  9. Jeroen says:

    Andarno,

    I did not quite mean it as a workaround, in fact I posted it to point out a problem area in working with interfaces and structs. If you want your thingamabob to act like a class, then you should máke it a class, and not make a struct and box it.

  10. The main reason to me, that you would most certainly want to make structs implement interfaces is to get the added advantage of generic parameter type constraints.

    The available options you have for type constraints are struct,class,new(), (Interfaces), and (abstract classes).

    To get better compile time checking you may want the method signature to look like this

    public static void Add<TEnum>(TEnum augend, TEnum addend)

      where TEnum : struct, IEnum

    {

       return augend.Add(addend);

    }

    Thus the caller is not allowed to pass in an integer as the most basic type constraint of just "struct" would allow.

    Interfaces to the rescue?

    thoughts?

  11. Zhi Wen says:

    nice post, I need some kind of polymorphism to store many different kind of structs in a single list and found your post.  I don’t need to modify the struct so interface will work perfectly for me.  Thanks.

  12. Zhi are you sure that your approach is not going to be a perf pitfall? Ensure that boxing doesn’t become a blocker for you

  13. Zhi Wen says:

    Just did some more reading.  

    >>I need some kind of polymorphism to store many >>different kind of structs in a single list and found >>your post.  I don’t need to modify the struct so >>interface will work perfectly for me.

    Actually, this is probably a bad idea.  It will probably store each struct as a reference type on heap.  I am not sure.  This whole thing is just confusing.

  14. molafish says:

    structs are value types, so they will be created on the stack. However, if the struct is created within the confines of a reference type, like a list, they will be allocated on the heap.

  15. GinoBambino says:

    I can't reproduce this pitfall anymore. It seems that the .NET compiler team has fixed this issue.

  16. Chris Susie says:

    I can think of a strong reason not already mentioned arguing in favor of structs implementing interfaces:

    Suppose a struct contains a member that is a pointer to a non-managed object.  You would certainly want that struct to be able to implement IDisposable so resources can be cleaned up.