无法从CLR委托中删除IronPython实例方法

时间:2011-03-12 15:01:57

标签: methods clr ironpython handler

我希望你们中的一些人可以在这里帮助我,因为我在这个问题的最后。看起来我可以添加instancemethod委托,但请注意删除它们。对委托的对象引用肯定是一样的吗?

这是一个错误的蒸馏再现:鉴于这个简单的小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 个答案:

答案 0 :(得分:1)

我还没有找到一个完美的答案,但这接近于解决问题。

回顾一下,问题在于当您将Python实例方法传递到CLR并尝试将其附加到委托时,DLR将目标对象调用点包装为另一个类,该类充当调用动态事件的分段平台。现在,这导致了我们的问题,因为尽我所知,这是在语言中自动完成的,并且每次尝试并在CLR和DLR之间传递实例方法时都会创建一个不同的实例。

所以我提出了一个比DLR端参考存储更好的解决方案,它不会造成内存泄漏。前提是它将尝试在当前调用列表中找到类似的委托。这将执行基本检查以查找传递的委托与现有委托列表之间的匹配。然后,如果没有找到它,它将深入研究IronPython基本代码并进行翻找,比较一些事情。如果它找到匹配,则删除该实例。

这是功能: `         ///         ///查找绑定到与参数相同的实例/方法的现有委托。         ///         ///这对于解决http://ironpython.codeplex.com/workitem/30338描述的问题非常有用         ///委托查找现有副本(数据方式,不是参考方式)。         ///如果找不到,则为空,否则返回现有的委托。         内部ActionSignature findDelegate(ActionSignature kInstance)         {             //跳过空数据。             if(kInstance == null)                 return 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;
    }

`

希望有人帮助! :)