Home > Mobile >  multible objects in one List
multible objects in one List

Time:12-07

is it possible to write the function below in just one foreach loop? i have already try it with List<object> but i do not bring this to work.

private void SoundsOnButtons()
{
    Button[] buttons = Resources.FindObjectsOfTypeAll<Button>();
    Toggle[] toggles = Resources.FindObjectsOfTypeAll<Toggle>();

    foreach (Button obj in buttons)
    {
        obj.gameObject.AddComponent<EventTrigger>();

        EventTrigger.Entry eventEnter = new EventTrigger.Entry();
        eventEnter.eventID = EventTriggerType.PointerEnter;
        eventEnter.callback.AddListener((eventData) => {
            Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
        });

        obj.GetComponent<EventTrigger>().triggers.Add(eventEnter);
    }

    foreach (Toggle obj in toggles)
    {
        obj.gameObject.AddComponent<EventTrigger>();

        EventTrigger.Entry eventEnter = new EventTrigger.Entry();
        eventEnter.eventID = EventTriggerType.PointerEnter;
        eventEnter.callback.AddListener((eventData) => {
            Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
        });

        obj.GetComponent<EventTrigger>().triggers.Add(eventEnter);
    }

}

thx for help. pezezzle

---------------EDIT---------------

Thanky you @derHugo this was exactly what i looking for but why is this not possible?:

    private void AddToList<T>() where T : Selectable
{
    List<T> list = new List<T>();

    Button[] buttons = Resources.FindObjectsOfTypeAll<Button>();
    Toggle[] toggles = Resources.FindObjectsOfTypeAll<Toggle>();

    list.AddRange(buttons); // Here i get a error cannot convert !
    list.AddRange(toggles); // Here i get a error cannot convert !

    foreach (var obj in list)
    {
        obj.gameObject.AddComponent<EventTrigger>();

        EventTrigger.Entry eventEnter = new EventTrigger.Entry();
        eventEnter.eventID = EventTriggerType.PointerEnter;
        eventEnter.callback.AddListener((eventData) => {
            Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
        });

        obj.GetComponent<EventTrigger>().triggers.Add(eventEnter);
    }
}

CodePudding user response:

You could, yes.

I know this is not exactly what your title is asking for but in my opinion your code would be already way better just extracting the inner block into a method and do

private void SoundsOnButtons()
{
    var buttons = Resources.FindObjectsOfTypeAll<Button>();
    
    foreach (var obj in buttons)
    {
        AddTriggerEvent(obj.gameObject);
    }

    var toggles = Resources.FindObjectsOfTypeAll<Toggle>();

    foreach (var obj in toggles)
    {
        AddTriggerEvent(obj.gameObject);
    }
}

private void AddTriggerEvent(GameObject target)
{
    var eventTrigger = target.AddComponent<EventTrigger>();

    var eventEnter = new EventTrigger.Entry();
    eventEnter.eventID = EventTriggerType.PointerEnter;
    eventEnter.callback.AddListener((eventData) => {
        Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
    });

    eventTrigger.triggers.Add(eventEnter);
}

This way you stay fully flexible and maintainable while it is as performant as doing all in a single loop - maybe even better since you don't waste any implementation or execution performance on uniting the two collections into one first.


You could even go one step further if you really want to and make your own generic wrapper method again like e.g.

// "Selectable" is the most specific common base class of UI.Button and UI.Toggle
// You could also use "Component" in order to just allow any component as type parameter
private AddEventTriggersToAll<T>() where T : Selectable
{
    var instances = Resources.FindObjectsOfTypeAll<T>();
        
    foreach (var obj in instances)
    {
        AddTriggerEvent(obj.gameObject);
    }
}

and then just do

private void SoundsOnButtons()
{
    AddEventTriggersToAll<Button>();
    AddEventTriggersToAll<Toggle>();
}

If you really want to go for a single loop you could use e.g. Linq Concat

foreach(var obj in Enumerable.Empty<Selectable>().Concat(buttons).Concat(toggles)
{
    AddTriggerEvent(obj.gameObject);
}

which basically equals more or less doing

var list = new List<Selectable>(buttons.Length   toggles.Length);
list.AddRange(buttons);
list.AddRange(toggles);

foreach(var obj in list)
{
    AddTriggerEvent(obj.gameObject);
}

As a very general note though: Are you sure you want to use Resources.FindObjectsOfTypeAll and not maybe rather simply Object.FindObjectsOfType?


Update - Answer to your edit

While you can add instances of Button or Toggle to a List<Selectable> you can not do so on a generic List<T> where T : Selectable because here you only limit the type downwards.

In other words since Button is a Selectable it would theoretically be totally valid to call AddToList<Button> BUT what you then would try to do is basically

List<Button> list = new List<Button>();

Toggle[] toggles = Resources.FindObjectsOfTypeAll<Toggle>();
list.AddRange(toggles);

=> can't work of course!

However, I don't see a need for your method to be generic at all ... why not simply have

private void AddToList()
{
    var list = new List<Selectable>();

    var buttons = Resources.FindObjectsOfTypeAll<Button>();
    var toggles = Resources.FindObjectsOfTypeAll<Toggle>();

    list.AddRange(buttons);
    list.AddRange(toggles);

    foreach (var obj in list)
    {
        obj.gameObject.AddComponent<EventTrigger>();

        EventTrigger.Entry eventEnter = new EventTrigger.Entry();
        eventEnter.eventID = EventTriggerType.PointerEnter;
        eventEnter.callback.AddListener((eventData) => {
            Sound.PlaySfxOneShot(Sound.GiveSfxSound(7));
        });

        obj.GetComponent<EventTrigger>().triggers.Add(eventEnter);
    }
}

BUT note that AddRange and working with List<T> in general is pretty expensive if you don't assign the expected capacity beforehand since while the list grows and internally exceeds the current maximum capacity it all the time has to reallocate and move around the underlying array!

You really gain nothing by "merging" both arrays together just to have a single loop. IF you want to go that way despite everything I said you should rather do

var buttons = Resources.FindObjectsOfTypeAll<Button>();
var toggles = Resources.FindObjectsOfTypeAll<Toggle>();

var list = new List<Selectable>(buttons.Length   toggles.Length);
list.AddRange(buttons);
list.AddRange(toggles);

as this way you at least allocate the list only once with the correct capacity right away

  • Related