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; publicvoid 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
Anonymous
October 05, 2005
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?Anonymous
October 05, 2005
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.Anonymous
October 06, 2005
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.Anonymous
October 06, 2005
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?Anonymous
October 07, 2005
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.Anonymous
December 20, 2007
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.Anonymous
March 20, 2008
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.Anonymous
April 15, 2008
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.Anonymous
June 11, 2008
The comment has been removedAnonymous
December 06, 2008
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?Anonymous
January 20, 2009
PingBack from http://www.hilpers.com/305229-valuetypeAnonymous
January 21, 2009
PingBack from http://www.keyongtech.com/658785-modyfing-value-types-through-anAnonymous
March 01, 2009
PingBack from http://www.necessaryandsufficient.net/2009/02/pop-quiz-applied-object-oriented-principles-with-c/Anonymous
April 28, 2009
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.Anonymous
April 28, 2009
Zhi are you sure that your approach is not going to be a perf pitfall? Ensure that boxing doesn't become a blocker for youAnonymous
May 26, 2009
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.Anonymous
February 02, 2010
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.Anonymous
June 28, 2014
good explanationAnonymous
December 04, 2014
I can't reproduce this pitfall anymore. It seems that the .NET compiler team has fixed this issue.Anonymous
December 17, 2014
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.