Dec 9, 2008

Structs and interfaces

I ran into an interesting tidbit the other day: a couple of structs implementing and interface. That just seems like asking for trouble to me, structs are value types and interfaces are reference types. Something tells me there could be some subtle issues associated with this. It’s pretty clear that boxing will occur when you pass a value type as a reference type, but I’m sure that’s not the only issue we’ll see here. Time for a quick study.

Here’s the interface we’ll be implementing:

   1: public interface IPlayer
   2: {
   3:     string Name { get; set; }
   4:     string Club { get; set; }
   5:     int Age { get; set; }
   6:  
   7:     void Transfer(string newClub);
   8: }

And here’s a real simple implementation for it:

   1: public struct Player : IPlayer
   2: {
   3:     public string Name { get; set; }
   4:     
   5:     private string club;
   6:     public string Club
   7:     {
   8:         get { return club; }
   9:         set { club = value; }
  10:     }
  11:  
  12:     public int Age { get; set; }
  13:  
  14:     public void Transfer(string newClub)
  15:     {
  16:         club = newClub;
  17:     }
  18: }

So we’re just going to go ahead and create an instance of a player. Once we have that we’ll print the club he’s currently playing for and we’ll transfer him to a different club (and make a lot of money in the process).

   1: Player player = new Player {Age = 20, Club = "AC Milan", Name = "Marco van Basten"};
   2: player.Transfer("Ajax Amsterdam");
   3:  
   4: Console.WriteLine(player.Club);

It’s not too hard to guess this will output “Ajax Amsterdam”. Now what happens if we transfer Marco back to AC Milan, but using the IPlayer interface now? Let’s see.

   1: IPlayer copy = player;
   2: copy.Transfer("AC Milan");
   3:  
   4: Console.WriteLine(player.Club);

Looks like Marco still plays for Amsterdam even though we  just transferred him to Milan. Tough! Well, what happened? The implicit cast created a temporary object we updated and threw away. Not good. Now if we changed the Player struct to a class it would all work as expected.

I would stay away from structs implementing interfaces, just make it a class instead.