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
{
publicvoid 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#.