2011-03-12 62 views
0

我希望你们中的一些人能够帮助我,因为我正在用这个方法结束我的智慧。看来我可以添加instancemethod委托,但请注意删除它们。对代表的对象引用是相同的,当然?IronPython实例方法无法从CLR代理中移除

这里是错误的蒸馏水再现:鉴于这种简单的小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

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

回想一下,问题是,当您将Python实例方法传递给CLR并尝试将其附加到委托时,DLR会将目标对象调用网站与另一个用作调用平台的类调用动态事件。现在,这引起我们的问题,因为尽我所知,这是在语言中自动完成的,并且每次我们尝试并在CLR和DLR之间传递实例方法时创建一个不同的实例。

所以我想出了一个比DLR参考存储更好的解决方案,它不会产生内存泄漏。前提是它会尝试在当前调用列表中找到类似的委托。这将执行基本检查以查找通过的委托和现有的委托列表之间的匹配。然后,如果没有找到它,它会钻研IronPython的基本代码,并围绕一些东西进行比较,比较几件事情。如果它找到匹配项,则删除该实例。

下面是函数: ' /// ///查找绑定到相同的实例/方法作为参数一个现有的委托。 /// ///这对于解决所述问题非常有用http://ironpython.codeplex.com/workitem/30338 ///代理可以查找现有数据(数据明智,不明智参考)。 ///如果找不到一个,则返回null,否则返回现有的委托。 内部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; 
    } 

`

希望帮助别人! :)