несколько объектов в одном списке

#c# #unity3d

#c# #unity3d

Вопрос:

возможно ли написать приведенную ниже функцию всего за один цикл foreach? я уже пробовал это, List<object> но я не привожу это в действие.

 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);
    }

}
 

спасибо за помощь.
pezezzzle

—————РЕДАКТИРОВАТЬ—————

Спасибо вам @derHugo это было именно то, что я искал, но почему это невозможно?:

     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);
    }
}
 

Комментарии:

1. Вы можете использовать List<GameObject> и AddRange все кнопки и все переключатели

2. Это работа для дискриминируемых объединений ! * убегает к ближайшему телефонному аппарату*

3. «но я не привожу это в действие». — как именно List<Object> не сработало?

4. obj.gameObject.AddComponent<EventTrigger>(); <— Я проверил документы Unity и Button не имеет никакого унаследованного свойства или члена gameObject — так откуда вы его берете?

5. Можно, но почему один цикл? Если вы просто хотите упростить код, лучшим выбором будет перенос объектов из цикла for в новый метод.

Ответ №1:

Вы могли бы, да.

Я знаю, что это не совсем то, о чем просит ваш заголовок, но, на мой взгляд, ваш код был бы уже намного лучше, просто извлеките внутренний блок в метод и выполните

 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);
}
 

Таким образом, вы остаетесь полностью гибкими и удобными в обслуживании, в то время как это так же эффективно, как делать все в одном цикле — может быть, даже лучше, поскольку вы не тратите впустую производительность реализации или выполнения на объединение двух коллекций в одну.


Вы могли бы даже пойти на один шаг дальше, если вы действительно хотите, и снова создать свой собственный универсальный метод-оболочку, например,

 // "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);
    }
}
 

а затем просто сделайте

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

Если вы действительно хотите использовать один цикл, вы можете использовать, например, Linq Concat

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

что в основном равно более или менее выполнению

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

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

В качестве очень общего замечания: вы уверены, что хотите использовать Resources.FindObjectsOfTypeAll , а не, может быть, довольно просто Object.FindObjectsOfType ?


Обновление — Ответ на вашу правку

Хотя вы можете добавлять экземпляры Button or Toggle в a List<Selectable> , вы не можете сделать это в общем List<T> where T : Selectable , потому что здесь вы ограничиваете только тип вниз.

Другими словами, поскольку Button это a Selectable , теоретически было бы вполне допустимо вызывать, AddToList<Button> НО то, что вы тогда попытались бы сделать, в основном

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

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

=> не может работать, конечно!

Однако я не вижу необходимости в том, чтобы ваш метод был универсальным вообще… почему бы просто не иметь

 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);
    }
}
 

НО обратите внимание, что AddRange работа с List<T> ними в целом довольно дорогая, если вы заранее не назначаете ожидаемую емкость, поскольку, пока список растет и внутренне превышает текущую максимальную емкость, ему все время приходится перераспределять и перемещать базовый массив!

Вы действительно ничего не получаете, «объединяя» оба массива вместе, просто чтобы иметь один цикл. ЕСЛИ вы хотите пойти по этому пути, несмотря на все, что я сказал, вам лучше сделать

 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);
 

таким образом, вы, по крайней мере, сразу выделяете список только один раз с правильной емкостью

Комментарии:

1. спасибо за ваш ответ, это то, что я ищу, можете ли вы также ответить на мое редактирование в моем вопросе? 🙂

2. @RonnyBigler смотрите Обновление внизу ответа

3. большое вам спасибо за ваше объяснение.