我终于找到了解决我的问题的方法!它涉及一点投射和反射,但实际上在我迄今为止所做的所有测试中都可以稳定运行。我进行如下操作:
R# 具有 IBulbItemsProvider 接口,其实现被动态加载并查询灯泡项,即,每当 R# 菜单之一打开时,菜单项包装灯泡动作。我现在实现了以下提供程序:
[SolutionComponent]
public class BulbItemInstrumentationComponent : IBulbActionprovider
{
public int Priority { get{ return int.MaxValue; } }
public object PreExecute(ITextControl tc) { return null; }
public void CollectActions(IntentionsBulbItems ibis, ...)
{
var bulbItems = ibis.AllBulbMenuItems;
foreach (var execItem in bulbItems.Select(item => item.ExecutableItem))
{
var proxy = execItem as IntentionAction.MyExecutableProxi;
if (proxy != null)
{
proxy.WrapBulbAction();
continue;
}
var exec = execItem as ExecutableItem;
if (exec != null)
{
exec.WrapBulbAction();
continue;
}
throw new Exception("unexpected item type: " + execItem.GetType().FullName);
}
}
}
Priority 的返回值确保我的提供程序在所有现有提供程序中被调用。这导致 CollectAction() 的参数 IntensionsBulbItems 已经包含从其他提供程序收集的 BulbMenuItems。
然后我遍历所有这些项目并安装我的观察包装器。因此,我需要将项目可执行文件向下转换为它们的实际类型。对于来自 R#Core 的灯泡项目,上述两种类型似乎是唯一使用的类型。来自 R#Extensions 的提供程序实际上可能使用其他类型,在这种情况下,循环结束时的异常将在未来被更慷慨的处理所取代;)
包装本身在 WrapBulbAction() 扩展方法中实现。这些看起来如下:
static class BulbItemInstrumentationExtensions
{
private static readonly MethodInfo MyExecutableProxiBulbActionSetter =
typeof (IntentionAction.MyExecutableProxi).GetProperty("BulbAction").GetSetMethod(true);
private static readonly FieldInfo ExecutableItemActionField =
typeof (ExecutableItem).GetField("myAction", BindingFlags.NonPublic | BindingFlags.Instance);
public static void WrapBulbAction(this IntentionAction.MyExecutableProxi proxi)
{
var originalBulbAction = proxy.BulbAction;
var bulbActionProxy = new LoggingBulbActionProxy(originalBulbAction);
MyExecutableProxiBulbActionSetter.Invoke(proxy, new object[] {bulbActionProxy});
}
public static void WrapBulbAction(this ExecutableItem exec)
{
var originalAction = (Action) ExecutableItemActionField.GetValue(exec);
var actionProxy = new LoggingActionProxy(originalAction);
var newAction = (Action) (wrapper.Execute);
ExecutableItemActionField.SetValue(exec, newAction);
}
}
LoggingBulbActionProxy 实现 IBulbAction 本身并将所有调用委托给传递给其构造函数的 IBulbAction,从而记录对 Execute() 的所有调用。LoggingActionProxy 是一个声明 Execute() 方法的类,它处理日志记录,然后委托给传递给构造函数的 Action。
现在我终于可以记录 R#BulbActions 的所有用法了。很难找到自己的方式,但最终战胜了挑战。希望这可以帮助某人,某处,某时!