我希望你们中的一些人可以在这里帮助我,因为我在这个问题的最后。看起来我可以添加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
看起来实例方法以某种方式被更改,因为它们被传递到函数中,并且不同的对象引用正在进行调用列表。我觉得我错过了一些愚蠢的东西!
干杯,
约翰
答案 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;
}
`
希望有人帮助! :)