Как преобразовать подписку на лямбда-событие, чтобы можно было отменить ее? - PullRequest
0 голосов
/ 07 марта 2019

Я очень новичок в мероприятиях / делегатах, поэтому извините, если я использую неправильную терминологию.

Я использую скрипт инвентаризации для Unity, который использует события / делегаты C # для подписки на событие щелчка правой кнопкой мыши на слоте предмета.

Проблема в том, что когда я динамически добавляю новые слоты элементов, мне нужно добавить обработчики событий в новые слоты. Если бы я просто запустил UpdateEvents (), те, которые были там в первую очередь, теперь имеют дублирующие триггеры.

В текущем коде используется лямбда-синтаксис, и я изучил эти темы о том, как создать экземпляр делегата:

Вот оригинальная лямбда-подписка:

// This is the lambda expression that I want to unsubscribe to
ItemSlots[i].OnRightClickEvent += slot => EventHelper(slot, OnRightClickEvent);

Вот что я попробовал, и я пометил ** на частях, которые моя IDE выделяет как неправильные:

// Try 1
EventHandler lambda = slot => EventHelper(slot, OnRightClickEvent);
ItemSlots[i].OnRightClickEvent += lambda;

// Try 2
EventHandler handler = (sender, e) => EventHelper(sender, OnRightClickEvent);
ItemSlots[i].OnRightClickEvent += handler;

// Try 3    
var myDelegate = delegate(sender, e) { EventHelper(**e**, OnRightClickEvent); };
ItemSlots[i].OnRightClickEvent += myDelegate;

Я также пытался преобразовать его без использования лямбды, но он не работает, как должно Я не уверен, что означает «слот» в лямбде. Это экземпляр, инициирующий событие? Вот что не сработало, но не выдало ошибок:

// Try without lambda
ItemSlots[i].OnRightClickEvent      += OnRightClickEvent;

Вот сокращенная версия полного кода. Я не до конца понимаю, как работает метод EventHelper (), но кажется, что это какой-то ярлык для проверки на ноль.

using System;
using System.Collections.Generic;
using UnityEngine;

public abstract class ItemContainer : MonoBehaviour, IItemContainer {
    public List<ItemSlot> ItemSlots;

    // There are really 8 event here, but I simplified it
    public event Action<BaseItemSlot> OnRightClickEvent;

    protected virtual void Awake() {
        UpdateEvents();
        SetStartingItems();
    }

    public virtual void UpdateEvents() {
        for (int i = 0; i < ItemSlots.Count; i++) {
            // This is the lambda expression that I want to unsubscribe to
            ItemSlots[i].OnRightClickEvent += slot => EventHelper(slot, OnRightClickEvent);
        }
    }

    private void EventHelper(BaseItemSlot itemSlot, Action<BaseItemSlot> action) {
        if (action != null)
            action(itemSlot);
    }
}

Ответы [ 2 ]

1 голос
/ 07 марта 2019

Давайте сделаем шаг за шагом:

// This is the lambda expression that I want to unsubscribe to
ItemSlots[i].OnRightClickEvent += slot => EventHelper(slot, OnRightClickEvent);

на левой стороне у вас есть событие (OnRightClickEvent). Проблема с правой стороной заключается в том, что разработчики на C # и .NET зашли слишком далеко в упрощении синтаксиса кода, который становится все труднее понять. В основном вы можете расширить до:

ItemSlots[i].OnRightClickEvent += (slot) =>  { EventHelper(slot, OnRightClickEvent); };
    ItemSlots[i].OnRightClickEvent += delegate(slot){ EventHelper(slot, OnRightClickEvent);};

Это то же самое. И если это все еще натянуто:

private void ItemSlot_OnRightClickEvent(BaseItemSlot slot)
{
     EventHelper(slot, OnRightClickEvent);
}    

тогда вы назначаете:

ItemSlots[i].OnRightClickEvent += ItemSlot_OnRightClickEvent;

slot - это параметр, который будет получен из объекта ItemSlots, вызывающего событие. Этот также лучше удалить слушателя:

ItemSlots[i].OnRightClickEvent -= ItemSlot_OnRightClickEvent;

С помощью анонимного метода вы не можете удалить метод, так как у вас нет на него указателя. А поскольку это событие, вы можете стереть все событие только с класса-владельца (в данном случае ItemSlot).

Я думаю, что лучше всего сохранять простоту и использовать старый синтаксис. Например, Visual Studio автоматически предложит правильную подпись метода, используя вкладку для автозаполнения, когда вы начнете писать:

 ObjectName.EventName += [tab]

Visual studio сгенерирует метод с именем ObjectName_EventName с правильным возвращаемым значением и параметром.

0 голосов
/ 07 марта 2019

Обычный шаблон с событиями C # таков:

    public event Action SomethingHappened;
    void OnSomethingHappened()
    {
        if (SomethingHappened!= null) SomethingHappened();
    }

    public void DoSomething()
    {
        // Actual logic here, then notify listeners
        OnSomethingHappened();
    }

Сначала вы объявляете событие, обычно называемое с использованием прошедшего времени. Затем закрытый метод используется для запуска события, обычно называемого с префиксом On (его также можно защитить, если производные классы должны запускать его). Наконец, открытый метод для внешних абонентов, чтобы заставить что-то произойти, и в нем вы уведомляете слушателей, запускающих событие.

Это событие можно передать как классам ItemSlot, так и ItemContainer:

    public event Action<BaseItemSlot> RightButtonClicked;
    void OnRightButtonClicked(BaseItemSlot item)
    {
        if (RightButtonClicked != null) RightButtonClicked(item);
    }

Затем вам нужно звонить ItemContainer.OnRightButtonClicked каждый раз, когда срабатывает ItemSlot.RightButtonClicked.

Итак, вы отправляете в эфир события:

    void OnEnable()
    {
        for (int i = 0; i < ItemSlots.Count; i++)
        {
            ItemSlots[i].RightButtonClicked += OnRightButtonClicked;
        }
    }
    void OnDisable()
    {
        for (int i = 0; i < ItemSlots.Count; i++)
        {
            ItemSlots[i].RightButtonClicked -= OnRightButtonClicked;
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...