Home > Software engineering >  How to "return" multiple times with for loop?
How to "return" multiple times with for loop?

Time:09-22

Hopefully this post gives more clarity as to what I am trying to achieve.

Objective: I want to spawn 20 apples(that have an attached button) from a list at runtime. When the apples are clicked they will spawn a popup with information pertaining to the apple that was clicked.

What I'm doing currently: I am using a for loop to run through the list to spawn the apples. I currently have the following code:

public class AppleInventory : MonoBehaviour
{
   [SerializeField] private ApplesScript applPrefab;
   [SerializeField] private Transform applParent;
   
    public ApplesScript CreateApples()
    {
        var appl = Instantiate(applPrefab, applParent);

        for (int i = 0; i < apples.Count; i  )
        {
            appl = Instantiate(applPrefab, applParent);
            appl.InitAppleVisualization(apples[i].GetAppleSprite());
            appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(i));
            appl.transform.position = new Vector2(apples[i].x, apples[i].y);
        }

        return appl;
    }
}

The Problem: The problem is that when I use the for loop and click on the button,it returns the following error: ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. The popup information also does not update.

Code without for loop: The code works to spawn one apple when I remove the for loop and set the int i = to a specific number, like below. It will give the correct popup info for any number that "i" is set to. This lets me know that it is not the rest of the code that is the issue. This leads me to believe it is the "return" line along with the for loop that is the issue. It seems I may need to "return" for each iteration but I am unsure of how to go about doing this.

public ApplesScript CreateApples()
{
   int i = 7;

   var appl = Instantiate(applPrefab, applParent);
   appl.InitAppleVisualization(apples[i].GetAppleSprite());
   appl.AssignAppleButtonCallback(() => CreateApplePopUpInfo(i));
   appl.transform.position = new Vector2(apples[i].x, apples[i].y);

   return appl;
}

Thank you,

CodePudding user response:

You have two options. The conventional option is to create all the items first and then return them all in some sort of list, e.g.

public static void Main()
{
    foreach (var thing in GetThings(5))
    {
        Console.WriteLine(thing.Number);
    }

    Console.ReadLine();
}

public static Thing[] GetThings(int count)
{
    var things = new Thing[count];

    for (var i = 0; i < count; i  )
    {
        things[i] = new Thing { Number = i };
    }

    return things;
}

The more modern option is to use an iterator. It actually will return one item at a time. It has the limitation that you have to use the items there and then - you won't have random access like you would an array or the like - but it also has advantages, e.g.

public static void Main()
{
    foreach (var thing in GetThings(5))
    {
        Console.WriteLine(thing.Number);
    }

    Console.ReadLine();
}

public static IEnumerable<Thing> GetThings(int count)
{
    for (var i = 0; i < count; i  )
    {
        var thing = new Thing { Number = i };

        yield return thing;
    }
}

The result of an iterator will usually be used as the source for a foreach loop or a LINQ query. Note that you can always call ToArray or ToList on the result of an iterator if you do want random access in specific situations, but you still have the advantages of an iterator elsewhere. For instance, let's say that your method produces 1000 items and you want to find the first one that matches some condition. Using my first example, you would have to create all 1000 items every time, even if the first one was a match. Using an iterator, because the items are processed as they are created, you can abort the process as soon as you find a match, meaning that you won't unnecessarily create the remaining items.

Note that my examples use the following class:

public class Thing
{
    public int Number { get; set; }
}

You can copy and paste the code into a Console app that doesn't use top-level statements. The bones of the code will still work with top-level statements, but you'll need to make a few other modifications.

CodePudding user response:

Store each separate "appl" that gets instantiated in an Array, ie appls[i]=appl

Do this within the for loop.

If you think about it, by putting the line "return appl;" outside the for loop, you are only storing that last game object, not all of them. Thats why creating an array of gameobjects and assigning them within the loop may work for you.

  • Related