Dela via


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