2009-06-07 87 views
6

有誰知道用RealProxy攔截dynamic方法調用(特別是那些將要調用RuntimeBinderException的調用)的方法嗎?我希望能夠捕獲異常並在其上實現「方法缺失」,但是在攔截器獲取參數之前它似乎會被拋出。C#4.0中缺少方法的困難:動態還是RealProxy

我的測試只是看起來像:

dynamic hello = MethodMissingInterceptor<DynamicObject>.Create(); 
Assert.AreEqual("World", hello.World()); 

World未在DynamicObject實際執行。攔截器是非常簡單的 - 我希望檢查IMethodReturnMessage.ExceptionRuntimeBinderException,並轉發到類似:

public IMessage MethodMissing(IMethodCallMessage call) 
{ 
    return new ReturnMessage(call.MethodBase.Name, new object[0], 0, call.LogicalCallContext, call); 
} 

不幸的是,我在攔截看到一些調用GetType,而不是不存在的World方法。

失敗 - 有誰知道是否有DynamicProxy版本在.NET 4.0上運行愉快,但可能解決了這個問題?

回答

17

我會從很長的答案開始。動態操作在C#中的每個綁定確實大約這三樣東西按以下順序:

  1. 詢問對象,如果它實現IDynamicMetaObjectProvider或者是一個COM對象綁定本身,如果失敗,然後...
  2. 使用反射將操作綁定到plain-old-clr對象上的操作,如果該操作失敗,則...
  3. 返回一個DynamicMetaObject,表示完全失敗綁定。

您看到GetType調用,因爲在第2步中,C#運行時綁定器正在反映出您試圖弄清楚您是否有適合調用的「World」方法,而這是因爲Hello的IDynamicMetaObjectProvider實現(如果有的話)不能提出任何特殊的操作。

不幸的是,在拋出RuntimeBinderException時,我們不再具有約束力。響應由於步驟3返回的元對象,動態操作的執行階段出現異常。您唯一能夠抓住它的機會是在實際的調用站點。

因此,如果您想要在C#中實現method_missing,那麼這種策略不會對您有用。你確實有一些選擇。

一個簡單的方法是在您的MethodMissingInterceptor中實現IDynamicMetaObjectProvider,並遵循包裝對象的IDMOP實現。如果內部IDMOP部分發生故障,您可以綁定到任何您想要的(可能是調用存儲在攔截器中的method_missing委託)。這裏的缺點是,這隻適用於已知爲動態對象的對象,例如,那些實現IDMOP的開始。這是因爲你基本上將自己插入到步驟1和步驟2之間。

另一個我能想到的方法是實現IDynamicMetaObjectProvider,並在其中響應每個綁定,並將調用返回給(a)產生的方法(C)編譯器將首先綁定的代碼相同,(b)捕獲RuntimeBinderException來調用method_missing方法。這裏的缺點是它會非常複雜 - 您需要生成任意的委託類型和使用它們的IL,針對C#運行時綁定程序集中的公共類型,坦率地說,它們不適用於公共消費。但至少你會在所有操作中缺少方法。

我確定還有其他的策略我沒有想到,比如你似乎暗示使用遠程代理。我無法想象他們的樣子,但我不能說他們是否會成功。

問題的關鍵在於C#4.0沒有一個設計能夠預測你的願望。具體而言,您不能輕易將自己插入到步驟2和步驟3之間。這使我想到了簡短的答案,很抱歉,C#4.0沒有method_missing。

+0

感謝您的出色解釋,克里斯 - 我剛剛開始在您的博客上瀏覽一系列C#'動態'貼子。 :) 出於我的目的,你的第一個解決方案聽起來像應該工作。我只是真的想要爲構建器樣式的對象進行這些調用,並且爲了增加測試API的流暢性,我不需要在任意對象上捕獲它們。 – Thom 2009-06-10 09:03:53