Методы экземпляра IronPython не могут быть удалены из делегата CLR - PullRequest
0 голосов
/ 12 марта 2011

Я надеюсь, что некоторые из вас могут помочь мне здесь, так как я нахожусь в конце этого ума.Кажется, что я могу добавить делегаты экземпляра метода, но обратите внимание, удалить их.Ссылка на объект для делегата такая же, конечно же?

Вот подробное воспроизведение ошибки: Учитывая этот простой маленький класс C #:

public class TypedEvent<T1> : TypedEventBase {
/** A definition of the function signature. */
public delegate void ActionSignature(T1 kParam1);

/** @brief A reference to the delegate which stores our handles. */
protected ActionSignature pAction = null;

public virtual bool addHandler(ActionSignature kHandler)
{
    // If we are already contained in the list then we don't need to be added again.
    if (pAction != null)
    {
        if (this.pAction.GetInvocationList().Contains(kHandler))
            return false;
    }

    // Add us to the list and return success.
    this.pAction += kHandler;
    return true;
}

public virtual bool removeHandler(ActionSignature kHandler)
{
    // If we have no handles return false.
    if (pAction == null)
        return false;

    // If we do not contain the handler then return false.
    if (!this.pAction.GetInvocationList().Contains(kHandler))
        return false;

    // Remove the handler and return true.
    this.pAction -= kHandler;
    return true;
}

public void invoke(T1 kParam1)
{
    if (this.pAction != null)
        this.pAction(kParam1);
}

}

Это работает как ожидалось:

## -- Procedural functions (function) work. ---
a = App.TypedEvent[object]()

def test(s):
    print s
    a.removeHandler(test)
    a.addHandler(test)

a.addHandler(test)

# Output
a.invoke("Hello")
>>> Hello
a.invoke("Hello")
>>> Hello

, как это:

## -- Static methods (unbound) work. ---
a = App.TypedEvent[object]()

class Foo:
    @staticmethod
    def test(s):
        print s
        a.removeHandler(Foo.test)
        a.addHandler(Foo.test)

a.addHandler(Foo.test)

# Output
a.invoke("Hello")
>>> Hello
a.invoke("Hello")
>>> Hello

, но это не работает:

## -- Instance methods (bound) do not work. --
a = App.TypedEvent[object]()

class Foo:
    def test(self, s):
        print s
        a.removeHandler(self.test)
        a.addHandler(self.test)

f = Foo()
a.addHandler(f.test)

# Output
a.invoke("Hello")
>>> Hello
a.invoke("Hello")
>>> Hello
>>> Hello
a.invoke("Hello")
>>> Hello
>>> Hello
>>> Hello
>>> Hello

Похоже, что методы экземпляра как-тоизменяются, когда они передаются в функцию, а ссылки на различные объекты составляют список вызовов.У меня такое чувство, что мне не хватает чего-то глупого!

Приветствия,

Джон

1 Ответ

1 голос
/ 29 декабря 2011

Я еще не нашел идеального ответа, но это близко к решению проблемы.

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

Итак, япридумали более приятное решение, чем хранение ссылок на стороне DLR, которое не приведет к утечкам памяти.Предполагается, что он попытается найти аналогичного делегата в текущем списке вызовов.Это выполнит основные проверки, чтобы найти соответствие между переданным делегатом и существующим списком делегатов.Затем, если ничего не найдено, он углубится в базовый код IronPython и покопается, сравнивая несколько вещей.Если он находит совпадение, он удаляет этот экземпляр.

Вот функция: `/// /// Найти существующий делегат, связанный с тем же экземпляром / методом, что и параметр./// /// Это полезно для обхода описанной проблемы http://ironpython.codeplex.com/workitem/30338 /// Делегат для поиска существующей копии (по данным, а не по ссылкам)./// Null, если он не был найден, в противном случае вернуть существующий делегат.внутренняя ActionSignature findDelegate (ActionSignature kInstance) {// Пропустить нулевые данные.if (kInstance == null) возвращает null;

        // Otherwise get the invocation list from our multicast delegate.
        var lInvocationList = pAction.GetInvocationList();
        ActionSignature kExisting = null;

        // Do the most basic check (i.e. is our object reference stored in here already!)
        if (lInvocationList.Contains(kInstance))
            return kInstance;

        // Go through and find if one already stored matches our new instance.
        foreach (var kIter in lInvocationList)
        {
            // Cast to our type.
            var kIterAS = kIter as ActionSignature;

            // Firstly, check our methods are the same.  This works for all.
            if (kIterAS.Method == kInstance.Method)
            {
                // Now check the targets match (this way works for IPYs staticmethods and functions).
                if (kInstance.Target.Equals(kIterAS.Target))
                {
                    // We matched, so save and break.
                    kExisting = kIterAS;
                    break;
                }

                // Now check if the targets match as instancemethods.
                // This is to get around a problem with IronPython where instancemethods
                // cannot be removed from CLR delegates.  See here:
                // http://ironpython.codeplex.com/workitem/30338
                var oarr_dd = kIterAS.Target as object[];
                var oarr_kh = kInstance.Target as object[];
                if (oarr_dd != null && oarr_dd.Length > 0 && oarr_kh != null && oarr_kh.Length > 0)
                {
                    IronPython.Runtime.Method m_dd = oarr_dd[0] as IronPython.Runtime.Method;
                    IronPython.Runtime.Method m_kh = oarr_kh[0] as IronPython.Runtime.Method;
                    if (m_dd != null && m_kh != null)
                    {
                        if (m_kh.im_self == m_dd.im_self)
                        {
                            // We matched, so save and break.
                            kExisting = kIterAS;
                            break;
                        }
                    }
                }

                // If we ended up here, we have no match so we can assume this is not the delegate
                // we are looking for!
            }
        }

        // Now if our matched delegate is null, it is not found.
        return kExisting;
    }

`

Надеюсь, это кому-нибудь поможет!:)

...