65.9K
CodeProject 正在变化。 阅读更多。
Home

为 VS.NET 构建重构插件 - 续集

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (31投票s)

2004 年 8 月 16 日

7分钟阅读

viewsIcon

162827

downloadIcon

1900

介绍如何为原始的重构插件添加额外功能。

Sample Image

引言

本文基于现有的插件,展示了一个用于创建提供多种重构功能的插件的简单框架。它演示了如何与源代码模型交互,自动化源代码中的编辑功能,以及生成新类。

背景

一年前,我写了一篇文章,提供了一个 Visual Studio 插件,用于将变量重构为属性。自那时以来,这个插件在我工作中一直被广泛使用,并且我添加了其他几个重构功能。为此,我实现了一个小框架来简化添加其他重构功能。

该插件现在支持以下功能:

  • 创建属性 - 指向一个变量,将其转换为带有 setter 和 getter 方法的属性。
  • 创建函数 - 选择一段代码,将其转换为一个函数,并在原始位置插入对该函数的调用。
  • 创建集合类 - 选择一个类,并生成一个类型的集合类(基于 CollectionBase)。
  • 创建模拟对象 - 选择一个类(甚至框架类),并生成一个集合类,该类覆盖所有可用的函数和属性,以便您模拟该类。
  • 移动到基类 - 提取一个元素并将其移动到基类。
  • 这是什么 - 一个简单的演示,展示了如何获取代码中的编辑点并确定它是做什么的。

插件框架

当您创建一个新的项目来构建 VS 插件时,VS.NET 会生成一个名为 connect 的类。该类包含 VS.NET 将调用的所有必要接口。有三种类型的调用:

  1. 初始化和清理调用:这些在启动时、加载插件时或 VS.NET 关闭时发生。
  2. 查询调用:此调用经常发生,每当 VS.NET 需要知道是否在其弹出菜单中启用您的命令时。您的代码必须确定具体情况,然后可以响应一个状态。
  3. 执行调用:您的插件命令已被调用,您需要运行它。

您不需要为每个命令拥有一个 connect 类,但是如果您在此类中公开多个命令(如此处所示),则 connect 调用必须在 Query 和 Exec 调用中确定哪个命令受影响。

插件框架通过创建一个名为 VS-Plugin 的基类来解决这个问题。上面列出的每种类型的命令都实现为 VS-Plugin 的子类。connect 类现在只知道这些插件的集合,当 Query 或 Exec 调用发生时,它只需遍历其集合并将请求传递给相应的类。

Sample Image

VSPlugIn 类还包含几个实用函数,用于执行以下操作:

  • 当前高亮的是什么,类、函数等。
  • 将必要命令结构链接到 VS.NET 的能力。
  • 弹出对话框以向用户询问目标项目。
  • 在 VS.NET 的“输出”窗口中显示消息。
  • 等等。

大多数插件还有一个单独的类来执行实际工作 - 例如,MakeMockObject 插件类有一个名为 MockObjectBuilder 的类。这是一种简单常见的“关注点分离”,插件类的目的是理解请求的细节并将结果代码传递到正确的位置,而 MockObjectBuilder 类的目的是生成 Mock 类。

如何添加另一个插件

只需要几个步骤:

1. 创建一个新的 VSPlugIn 子类

VSPlugIn 有几个抽象属性和方法,您必须实现它们:

  • cmdName:简短命令,例如:“MakeProperty”。
  • qualifiedName:完全限定命令,例如:“RefactorAddIn.Connect.MakeProperty”。
  • IconId:显示在菜单中的图标 ID,例如:54。
  • position:命令在菜单中的位置(1=第一个)。
  • shortDescription:菜单中显示的文本。
  • long Description:工具提示的文本。
  • doQueryStatus():实现逻辑以确定方法当前是否有意义,是否应激活或非激活(或隐藏)。
  • doExec():实现命令的实际操作。

2. 扩展 Connect 类

connect 类有一个名为 OnStartupComplete 的方法。您必须将您的插件添加到其他插件的集合中,并调用方法使其显示在正确的工具栏上。

CommandBar popup = InsertSubMenu("Code Window", "Refactoring");
CommandBar classViewCommandBar = applicationObject.CommandBars["Class View Item"];

VSPlugIn plugin = new PropertyMaker(applicationObject, addInInstance);
Plugins.Add(plugin);
plugin.InsertCommand(classViewCommandBar,false);
plugin.InsertCommand(popup, false);

前两行已经给出,它们提供了两个命令栏,一个是独立的弹出菜单,属于代码窗口菜单(在代码窗口右键单击时弹出的那个),另一个是右键单击类资源管理器时弹出的菜单。

您需要添加与上面最后四行类似的​​代码:

  1. 创建您的插件实例。
  2. 将插件添加到插件集合中。
  3. 如果您希望插件在每个命令栏上都可用,请调用插件的 InsertCommand 方法。布尔值仅在开发期间才应为 true - 参见下面的“兴趣点”。

上面的示例使属性生成器在两个菜单上都可用。您可以定位其他菜单栏,也可以完全不放置它们。在这种情况下,您仍然可以通过“工具”->“选项”对话框(选择“环境”文件夹和“键盘”项)中的键盘映射来绑定到您的命令 - 或者您可以通过宏调用您的命令。

关注点

编程 Visual Studio.NET 不是一件容易的事。良好的文档不容易获得。然而,我发现“Inside Microsoft Visual Studio. NET”(Brian Johnson, Craig Skibo, Marc Young, Microsoft Press, ISBN 0-7356-1874-7)这本书非常有帮助。但您仍需要仔细阅读几遍。

Visual Studio .NET 是用 COM 编写的,这使得工作不那么愉快。以下是一些要点:

  1. 集合的索引从 1 开始。
  2. 如果您向一个集合请求它没有的东西,您不会得到 null 而是得到一个异常。所以如果您想检查某物是否在集合中,您必须这样写:
    protected bool HasCommand(string commName,out Command command)
    {
        command = null;
        try
        {
            Commands commands = _applicationObject.Commands;
            command = commands.Item(commName,-1);
            return true;
        }
        catch (System.Exception){}
        return false;
    }
  3. 并非所有命令在所有情况下都有效。例如,InsertClass 命令(用于将新类插入代码模型)仅适用于 C++,不适用于 C# 或 VB.NET。但是,InsertMethod 可以。
  4. 并非所有窗口都输出其内容。尽管文档非常努力地告诉我们如何访问某些窗口中的树形控件,但您需要仔细阅读才能发现,对于某些窗口(例如类视图窗口),这是不可用的。
  5. 强烈建议您研究和理解其他插件示例。非常感谢 Erick Sgarbi 和他关于 VS 插件的文章,我从中借鉴了几种技术,包括上面介绍的那种。
  6. 如果您对 CodeDom(定义代码结构然后生成源代码)感兴趣,请参考我的 CollectionBuilder 类作为示例。
  7. 当您将插件发布到 Visual Studio 时,您会创建一个称为命令的对象。该命令是 VS.NET 中其他方识别您的插件并调用它的结构。请注意,在关闭时,VS.NET 会保存此命令并在重新启动时重新加载它。因此,您不必在每次加载插件时都重新创建命令。实际上,您不应该这样做,因为这会扰乱系统。VSPlugIn::InsertCommand 方法有一个名为 deleteIfExists 的布尔参数;如果为 true,它将在创建新命令之前删除现有命令。此参数仅在开发和调试插件期间应为 true。在默认模式下(=false),它会尝试查找并使用现有命令,仅在不存在时创建新命令。
  8. 在调试期间,还可以创建多个控件实例(使命令在弹出菜单中可见的那些)。InsertCommand 方法尝试通过调用 flushControls() 来清除这些。如果您不这样做,可能会发生各种奇怪的问题,让您抓狂。

历史

  • 版本 1.1 - 基于反馈,我添加了几项更改:
    • 现在可以配置“创建属性”中私有变量的命名方式。
    • 安装程序会在注册表中添加一个条目:HKEY_CURRENT_USER\ Software\ MethodsConsulting\ RefactorAddIn\ PrivatevarCase。可能的值有:
      • 下划线 - 在变量前加上下划线
      • m_下划线 - 在变量名​​称前加上“m_”
      • 驼峰 - 使用驼峰命名法
    • “移动到基类”现在支持变量、属性和函数。
    • 以及修复了各种小烦扰。

尽情享受吧

© . All rights reserved.