C# for C++ Devs: Structs vs Classes


I’m from a C++ background and now I’m working quite a bit more with C# so I’m learning new things all the time. One thing that baffled me recently was caused by the difference between structs and classes in C#.


In C++ the only difference between struct and class is the default member accessibility. For example, in the code below A::f() is private, whereas B::f() is public


class A
{
   
void f();
}


struct B
{
   
void f();
}


That’s the only difference. Structs can have member functions and classes can contain only data members. For C#, things are different, as I found out recently.


In C#, structs are always passed by value, whereas classes are always passed by reference. What this means in practice is that anywhere you pass a struct to a function as a parameter or return it you are doing so by value.


The confusing piece of code for me was equivalent to the following:


struct Animal
{
   
public int Spots;
}


class Program
{
   
static void Main(string[] args)
    {
       
List<Animal> allAnimals = new List<Animal>();
        allAnimals.Add(
new Animal());
        allAnimals.Add(
new Animal());

       
foreach (Animal animal in allAnimals)
        {
            animal.Spots = 5;
       
}
      
Debug.WriteLine(String.Format(“First animal spots: {0}”, allAnimals[0].Spots));
    }
}


When I compiled the code above I got the error:


error CS1654: Cannot modify members of ‘animal’ because it is a ‘foreach iteration variable’


How strange, I thought. OK, maybe in a foreach loop you can’t modify public members. Let’s try calling a function instead: 


struct Animal
{
    public void setSpots(int NewSpots)
   
{
        Spots = NewSpots;
    }
   
public int Spots;
}


class Program
{
   
static void Main(string[] args)
    {
       
List<Animal> allAnimals = new List<Animal>();
        allAnimals.Add(
new Animal());
        allAnimals.Add(
new Animal());

       
foreach (Animal animal in allAnimals)
        {
            animal.setSpots(5);
       
}
      
Debug.WriteLine(String.Format(“First animal spots: {0}”, allAnimals[0].Spots));
    }
}


So the compile error went away, but the message printed out was:


First animal spots: 0


I was expecting 5 here. After reading a little bit about structs and classes in C#, the penny dropped. Each iteration through allAnimals was getting a copy of the animal and calling setSpots. If I changed the definition of Animal to a class instead of struct, I could use the original code.


class Animal
{
   
public int Spots;
}


class Program
{
   
static void Main(string[] args)
    {
       
List<Animal> allAnimals = new List<Animal>();
        allAnimals.Add(
new Animal());
        allAnimals.Add(
new Animal());

       
foreach (Animal animal in allAnimals)
        {
            animal.Spots = 5;
       
}
      
Debug.WriteLine(String.Format(“First animal spots: {0}”, allAnimals[0].Spots));
    }
}


Incidentally, members of structs also do not have default public accessibility in C#.

Comments (0)