Foreach, Switch and Else are Costly in Game Design! There exist Better Approaches!

No we don't mean that you cannot Use them. But you should try to  avoid them and break  down your C# Code. There are only a few Reasons  why you need to Use a Else Statement. Switch should be avoided since it is a very complex Structure inside  CSharp and is basicially nothing else then a if else Statement. While the Foreach Wraps around a while loop and produces extra garbage inside your Project.

For a Good and Performant Code Structure, Foreach, Switch and Else Should be avoided in Game Development

Micro Optimization Tips for CSharp inside Unity and .Net Framework 4.x

The CSharp compiler does something to our code called “lowering”.

As the compiler reduces the code, it breaks it down to the basic programming structure. The higher level your code is, the more the compiler has to reduce, resulting in poorer performance.

So we can help the compiler make our code as friendly to it as possible.

SharpLab is a Online Code Compiler which can be used for Code Lowering. With this Tool we can Lower the C-Sharp Code and see whats happening behind.

Lowering the Foreach and For Loop in CSharp

In newer Versions of .net Framework +5 the Foreach doesn't Allocate Memory anymore if you using Generic Types, it will make no difference which one is used then.

CSharp Code

				
					        object[] arrayForEach = new object[5];
        
        foreach(var item in arrayForEach)
        {
            
        }
        
        object[] arrayFor = new object[5];
        
        for(int i = 0 ; i < arrayFor.Length ; i++)
        {
            
        }  
				
			

Lowered CSharp Code

				
					        object[] array = new object[5];
        int num = 0;
        while (num < array.Length)
        {
            object obj = array[num];
            num++;
        }
        object[] array2 = new object[5];
        int num2 = 0;
        while (num2 < array2.Length)
        {
            num2++;
        }
				
			

The big difference between the For and ForEach loops is the reduced CSharp code in line 5.

Both loops are reduced to while loops. Now ask yourself why we don’t use while loops in the first place? The answer is simple, one little mistake and you are in an infinite loop and your program or game just freezes. In the earlier days of computers, the whole system would have frozen in this case.

So if you feel confident to work with while loops, you can do it, but it is not recommended and in general a bad Advice.

Line 5

The Foreach loop is downgraded to a while loop like the For loop, but the Foreach loop creates a local reference. This leads to additional garbage that needs to be collected during the Runtime. You can easily avoid this garbage by creating a For loop and accessing the values directly.

If you are working with an IDE like Visual Studio, you can create a ForEach loop and have Visual Studio convert the ForEach loop to a For loop for you.

The main advantage of a ForEach loop is that it is easier to read and handle than a For loop. But the ForEach loop comes with limitations.

The Evil Switch Statement, Is it?

The switch statement is primarily nothing more than an if/else statement. Unless you are working with non-integer values.

If you use strings, floats, or any other type besides integer values, the C# compiler generates a hash table for strings as an example, strange statements when using doubles, and so on. C# performs a number of calculations to sort out the switches.

In game development, especially mobile game development, this can consume quite a bit of power depending on how often the switch is called.

Using a Switch every 1 minute to compute something? Go ahead! Are you using a string switch or double switch inside a state machine or AI? Think about something else, is my recommendation since it gets called very frequently and can cause significant Performance issues.

Switch Approach

				
					        TestType testType = TestType.None;

        switch (testType)
        {
            case TestType.None:
                break;
            case TestType.One:
                break;
            case TestType.Two:
                break;
            default:
                break;
        }
				
			

If (NO ELSE) Approach

				
					        TestType testType = TestType.None;

        if (testType == TestType.None)
        {
            return;
        }
        if (testType == TestType.One)
        {
            return;
        }
        if (testType == TestType.Two)
        {
            return;
        }
				
			

Is the difference in readability really that big?

As long as you use integer values and (enums), the switch is perfectly fine.

However, if you are using a type other than integer, you should consider a different approach, solving your logic. I use regular if expressions, and if they are true, we can perform our action and exit the function. The same thing happens in a switch. The readability is almost identical, and you don’t need many more lines of code.

Depending on the size of your swich cases, it may be easier to debug a switch. However, it depends mostly on your project and how often the function is called with the switch statement.

Considering the size, it should be easier to debug a switch than if statements. Since the Switch Statement checks for Duplicate Cases, which is not the Case for If Else Statement.

Remember that this idea mostly applies to computer game development. In normal software development it is not necessary, because a few milliseconds more or less have no effect, the user is willing to wait for the results. This is not the case with Real Time Games.

Do you really need the Else Statement after your If

If you understand object-oriented programming, you know that each object has a task to perform. To keep it object-oriented, we divide the object into many functions and execute something in them. When the function has completed its task, we should terminate the function.

This is better than procedural programming where everything is read from top to bottom. So you need the else statement because the next time you go through that line of code, it can take a while. However, this approach is also possible in procedural programming, but it is not really advantageous.

If Else

				
					float AddEntityBySpace(int id, float amount, ref EntityBase entity, int i)
    {
        float amountToAdd = amount;
        bool addItems = true;

        while (addItems && amountToAdd > 0)
        {
            var space = ActualWeightandVolume();
            var spaceNeed = InventoryManager.Instance.GetEntitySpace(id);

            if (space.Weight + spaceNeed.Weight < inventoryMaxWeight && space.Volume + spaceNeed.Volume < inventoryMaxVolume)
            {
                //Enough Space to Add
                entity = entitiesInInventory[i];
                entity.amount += 1;
                entitiesInInventory[i] = entity;
                amountToAdd--;                
            }
            else
            {
                addItems = false;
            }
        }

        return amountToAdd;
    }
				
			

If, Continue, Return or Break

				
					float AddEntityBySpace(int id, float amount, ref EntityBase entity, int i)
    {
        float amountToAdd = amount;
        bool addItems = true;

        while (addItems && amountToAdd > 0)
        {
            var space = ActualWeightandVolume();
            var spaceNeed = InventoryManager.Instance.GetEntitySpace(id);

            if (space.Weight + spaceNeed.Weight < inventoryMaxWeight && space.Volume + spaceNeed.Volume < inventoryMaxVolume)
            {
                //Enough Space to Add
                entity = entitiesInInventory[i];
                entity.amount += 1;
                entitiesInInventory[i] = entity;
                amountToAdd--;
                continue;
            }
            addItems = false;            
        }

        return amountToAdd;
    }
				
			

To be Fair, I personally like While Loops

The two functions above illustrate the manageability of the else statement. We try to add the item as long as we have space in the inventory.

If we have space, add the item

Else End Loop Because no Space

In loops, including For and Foreach, you can manipulate the loop. As you can see in the second script, I use Continue instead of the else statement so that the loop starts from the beginning.

Use Continue, Break and Return

In CheckForType, where we iterate through the array, we can see a number of use cases in loops.

These statements help us ignore the Else statement in loops.

In a final example, we compare iterating through an inventory with If Else to an optimized No Else version.

For Demonstration purpose this code gonna use now foreach loops.

				
					    void CheckForType()
    {

        int[] array = new int[5];
        foreach (var item in array)
        {
            //Before we Reach Number 3 we Skip
            if (item < 3)
                continue;

            //After Reaching Number 3 we can get the remaining Numbers in the Array
            Debug.Log("Item is: " + item);
            //Item is: 3
            //Item is: 4
            //Item is: 5
        }

        foreach (var item in array)
        {
            if (item == 3)
                break; //We Stop the Loop if Item is 3
        }

        Debug.Log("Item is 3");
        //Item is: 3

        foreach (var item in array)
        {
            if (item == 3) //We End the Function if Item is 3
                return;
        }
    }
				
			

Remove Inventory Item If Else

				
					public bool RemoveEntityElse(int id, int amount = 1)
    {
        for (int i = 0; i < entitiesInInventory.Count; i++)
        {
            if (entitiesInInventory[i].id == id)
            {
                if (entitiesInInventory[i].amount < amount)
                {
                    return false;
                }
                else
                {
                    EntityBase entity = entitiesInInventory[i];
                    entity.amount -= amount;

                    if (entity.amount <= 0)
                    {
                        entitiesInInventory.RemoveAt(i);
                        return true;
                    }
                    else
                    {
                        entitiesInInventory[i] = entity;
                        return true;
                    }
                }
            }
        }
        return false;
    }
				
			

Remove Inventory Item Optimized with Statement and no Else

				
					   public bool RemoveEntity(int id, int amount = 1)
    {
        for (int i = 0; i < entitiesInInventory.Count; i++)
        {
            if (entitiesInInventory[i].id != id)
                continue;

            if (entitiesInInventory[i].amount < amount)
                return false;

            EntityBase entity = entitiesInInventory[i];
            entity.amount -= amount;

            if (entity.amount <= 0)
            {
                entitiesInInventory.RemoveAt(i);
                return true;
            }

            entitiesInInventory[i] = entity;
            return true;
        }
        return false;
    }
				
			

Leave you with ur own Conclusion

Although it is very easy to use the Else statement, most of the time it is not necessary. Because if you follow the object-oriented guidelines, a function should perform 1 task at best.

And either the function completes the task or you can exit the function with Return.

Not using the Else statement cleans up the code and helps against nesting. It saved 6 lines of readable code and the function still does the same thing while faster and easier to read, in my Opinion.

What are your thoughts about this approaches? Leave a Comment, Join our Discord or Reddit on our Post.

Addition, 27.05.22

I received a some bad words about this Post, because some people are disagree. I wanna clarify, this might not be the best Solution nor is it the Optimum Way. While Programming there are 1000 of different ways and approaches to get to the finish Line.

As long your Product is working at the end, you have done everything right, no matter how good or bad your Code is. Don’t let Toxic people stay in your Way!

Leave a Reply

Your email address will not be published.