抽象方法/接口实现宏






2.71/5 (7投票s)
用于自动实现由给定类或接口扩展的抽象方法的宏。
引言
因此... 我厌倦了为每个基于抽象类的类重新覆盖抽象方法,并决定编写以下宏,这样我就不必再这样做了 : )。
注释:
- 代码应该具有更好的模块化,并且将来会改进。 如果您想贡献,请联系我 --> yaron@valor.com。
- 使用 ProjectItem,这意味着它要求您从“类视图”中选择一个项目,然后右键单击它以获取上下文菜单,然后... 好吧,您会掌握它的。
- 设置也会自动删除自身,但编写得很糟糕。 但是,它可以完成工作。
安装
要安装,请下载 *vsmacros* ZIP 文件(如上所示),并将其解压缩到任何目录中。 然后转到 Visual Studio .NET,然后按 Alt-F8。 应该打开“宏”窗口。 选择最上面的元素(“宏”),右键单击并选择“加载宏项目...”,然后从您放置它的位置选择文件。 现在,您将在“宏”父项下获得一个名为 *CodeAssist* 的元素。 单击“设置”以将其绑定到您的解决方案和类视图的弹出菜单。 您已设置完毕!
用法
从您的解决方案或类视图中选择一个类,右键单击并选择“代码辅助 -> 实现抽象方法”。 这将自动从父抽象类和接口添加具有各自注释的方法。
欢迎并鼓励评论!!!
代码
首先,一些功能工具
checkLanguage
- 验证给定的文档是否为 C#。 否则会抛出异常。' Checks to see that we're using ' CSharp (otherwise, this thing WILL fail) Private Sub checkLanguage(ByVal document As Document) If document.Language <> "CSharp" Then Throw New System.Exception("This Macro only works on C# code") End If End Sub
hasFunction
- 验证类中是否存在函数(注意 - 这仍然是 WIP,因为它不检查同名、不同参数的重载)。 将来会改进。 为此,我们接收到一个ClassElement
(类型为CodeClass
),然后检索其类成员的列表。 然后我们遍历它们并找到同名的函数。 请注意,我们通过与vsCMElementFunction
进行比较来检查成员是否为函数。' Checks to see if function already exists in class 'TODO: Make this check parameters, as well Private Function hasFunction(ByVal classElement As CodeClass, _ ByVal functionElement As CodeFunction) Dim member As CodeElement For Each member In classElement.Members If (member.Kind = vsCMElement.vsCMElementFunction) Then If (member.Name = functionElement.Name) Then Return True End If End If Next Return False End Function
现在我们从整个事物的要点开始
processBaseClasses
- 这里的这段代码将处理基类和接口,并将它们的抽象函数或必须实现的方法添加到要实现的函数列表中。 首先,它接收一个函数ArrayList
- 请注意,此函数是递归的,因此这是所有要稍后处理的函数的唯一容器。 它还接收给定类包含的基类和已实现接口的列表。 这些将被遍历,并且它们各自的“MustImplement”函数将被添加到容器(functions)中,然后将被添加到类本身。该代码包含注释,可用于跟踪代码。 我会根据要求进行扩展。
' Recursively process base classes Sub processBaseClasses( _ ByVal functions As ArrayList, _ ByVal baseClasses As CodeElements, _ ByVal implementedInterfaces As CodeElements) ' Create elements array Dim baseElement As CodeElement ' Collect all items Dim elements As ArrayList = New ArrayList() For Each baseElement In baseClasses elements.Add(baseElement) Next If (Not implementedInterfaces Is Nothing) Then For Each baseElement In implementedInterfaces elements.Add(baseElement) Next End If ' Exit if done If (elements.Count = 0) Then Exit Sub End If ' Process base classes Dim cFunction As CodeFunction For Each baseElement In elements Debug.WriteLine(baseElement.Name) If (baseElement.Kind = vsCMElement.vsCMElementInterface) Then ' Recurse processBaseClasses( _ functions, _ baseElement.Bases, _ Nothing _ ) Else ' Recurse processBaseClasses( _ functions, _ baseElement.Bases, _ baseElement.ImplementedInterfaces) End If ' Check if this level implements lower ones Dim fCnt As Integer = 0 Do While fCnt < functions.Count cFunction = functions.Item(fCnt) If (baseElement.Kind = vsCMElement.vsCMElementClass) Then If (hasFunction(baseElement, cFunction)) Then functions.Remove(cFunction) End If Else fCnt += 1 End If Loop ' Get members Dim member As CodeElement For Each member In baseElement.Members If (member.Kind = vsCMElement.vsCMElementFunction) Then cFunction = member If (cFunction.MustImplement) Then functions.Add(cFunction) End If End If Next Next End Sub
- 这是单击“实现抽象方法”弹出菜单项时调用的子例程
- 它将处理给定文件中的类列表。
- 接收要实现的函数列表(参见上面的
processBaseClasses
) - 将它们添加到当前类。
' Impelements abstract methods for class Sub ImplementAbstractMethods() Try ' Life stinks... If (DTE.SelectedItems.Count <> 1) Then MsgBox("Function will only work on 1 file") Exit Sub End If ' Get current selection Dim currItem As ProjectItem = _ DTE.SelectedItems.Item(1).ProjectItem If (Not currItem.IsOpen) Then currItem.Open() End If Dim currDoc As Document = currItem.Document ' Check compatibility with language CheckLanguage(currDoc) ' Create arraylist to contain all abstracts items Dim functions As ArrayList = New ArrayList() Dim cFunction As CodeFunction ' Get namespace Dim fileCM As FileCodeModel = _ currDoc.ProjectItem.FileCodeModel Dim cNameSpace As CodeNamespace = _ fileCM.CodeElements.Item(1) ' Process classes Dim classElement As CodeClass For Each classElement In cNameSpace.Members processBaseClasses( _ functions, _ classElement.Bases, _ classElement.ImplementedInterfaces _ ) ' Process all members DTE.UndoContext.Open("Add Abstract Implementations") For Each cFunction In functions ' Check if exists in class If (Not hasFunction(classElement, cFunction)) Then ' Add function Dim newFunction As CodeFunction Dim type As String = cFunction.Type.AsString If (cFunction.Parent.Kind = _ vsCMElement.vsCMElementFunction) Then type = "override " + type End If ' Create function newFunction = classElement.AddFunction( _ cFunction.Name, _ vsCMFunction.vsCMFunctionFunction, _ cFunction.Type.AsString, _ -1, _ cFunction.Access _ ) ' Create comment block Dim commentString As String commentString = _ "Implementation of " + _ cFunction.FullName ' Add parameters Dim parameter As CodeParameter For Each parameter In cFunction.Parameters newFunction.AddParameter( _ parameter.Name, _ parameter.Type.AsString() _ ) ' Add parameter comment commentString += _ vbCrLf + "<param name=""" _ + parameter.Name + _ """></param>" Next ' now add comment newFunction.Comment = commentString End If Next ' Close undo context DTE.UndoContext.Close() Next Catch ex As System.Exception MsgBox(ex.ToString()) End Try End Sub
- 接收要实现的函数列表(参见上面的
- 它将处理给定文件中的类列表。
安装脚本(运行一次)
现在,这里的这段代码在创建时面临着相当大的挑战。 我一直在搜索这里的几个项目,直到最后我了解了事物的运作方式。 我将其分解为 3 个主要方法
GetCommandBars
- 检索解决方案和类弹出窗口的命令栏。AddMenu
- 删除旧的子菜单项(如果存在),并添加新的子菜单。AddCommand
- 将给定的宏命令添加到菜单项。
请注意 AddMenu
(addCommand
) 中的粗体行 -> 指向宏位置。 它当前指向一个名为 *VisualStudioMacros* 的项目。 如果这让您感到困惑或者您将宏放置在不同的项目中 - 请更改此设置。
Imports EnvDTE
Imports System.Diagnostics
Imports System.IO
Imports System.Text
Imports System.Collections
Imports Microsoft.Office.Core
Public Module Setup
Private Function getCommandBars() As ArrayList
Dim barList As ArrayList = New ArrayList()
' Locate solution and class view popups
Dim cmdBar As CommandBar
For Each cmdBar In DTE.CommandBars
Dim cmdBarCtl As CommandBarControl
If ((cmdBar.Name = "Item") Or _
(cmdBar.Name = "Class View Item")) Then
barList.Add(cmdBar)
End If
Next
' Couldn't find bars
If (barList.Count = 0) Then
Throw New _
System.Exception("Failed to locate Popup Menus!")
End If
' return bars
Return barList
End Function
' This should be run ONLY ONCE Private Sub AddMenu() Dim menu As CommandBarButton Dim cmdBar As CommandBar Dim cmdBars As ArrayList Dim cmdBarCtl As CommandBarControl ' Get command bars cmdBars = getCommandBars() ' Remove all oldies For Each cmdBar In cmdBars Dim ctrlCnt As Integer = 1 While (ctrlCnt <= cmdBar.Controls().Count) cmdBarCtl = cmdBar.Controls().Item(ctrlCnt) If (cmdBarCtl.Caption.IndexOf("Code-Assist") _ > -1) Then cmdBarCtl.Delete() Else ctrlCnt += 1 End If End While ' Create menu Dim menuPopup As CommandBarPopup menuPopup = _ cmdBar.Controls.Add(vsCommandBarType.vsCommandBarTypePopup) menuPopup.BeginGroup = True menuPopup.Caption = "Code-Assist" ' Add commands addCommand( _ "Implement Abstract Methods", _ "Macros.VisualStudioMacros.Coding.ImplementAbstractMethods", _ menuPopup _ ) Next End Sub
' Get command to add
Sub addCommand( _
ByVal caption As String, _
ByVal cmdName As String, _
ByVal menuPopup As CommandBarPopup)
Dim cmd As Command
Dim cmdBarCtl As Microsoft.Office.Core.CommandBarControl
' Get item
cmd = DTE.Commands.Item(cmdName)
' Add to command bar
cmdBarCtl = cmd.AddControl(menuPopup.CommandBar())
cmdBarCtl.Caption = "Implement Abstract Methods"
End Sub
' Sets up menus, ...
Sub Setup()
AddMenu()
MsgBox("Setup done!" & vbNewLine & _
"Check Item menu for Code-Assist entries")
End Sub
End Module
就是这样!
历史
- 已更新 -> 6 月 10 日,美国东部时间 9:30
- 已将源代码添加到文章中。
- 根据要求,现在添加了宏项目 (*.vsmacros*) 的 ZIP 文件。
- 已更新 -> 6 月 3 日,美国东部时间 9:30
- 已修复设置以永久删除菜单项,而不是暂时删除。
- 已将菜单项添加到类和解决方案项目视图中。