使用Visual Studio 2005 IDE的宏,自动为c#变量生成属性

在编写c#代码过程中,我们经常需要做一些重复枯燥的工作。例如,编写DTO(数据访问对象),通常就是为一个类定义一系列的变量和属性。

有一些第三方的IDE辅助工具,可以为我们生成一些代码,减少工作量。例如,Assist X就是一款很值得推荐的工具,使用其提供的Encapsulate Field功能,可以很方便地将一个类地编写封装为属性。

我今天需要介绍的如何使用Visual Studio 2005 IDE中自带的宏实现类似的功能。

打开Visual Studio 2005 IDE,选择“工具” > "宏" > “宏 IDE”,选择“添加模块”。例如,我是在MyMacros项目中新增了一个EditorHelper模块,代码如下:

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics

Public  Module EditorHelper
    
' 为一个参数封装一般属性访问器
     Public   Sub  EncapsulateField()
        
Dim  projectItem  As  ProjectItem  =  DTE.ActiveDocument.ProjectItem
        
Dim  fileCodeModel  As  FileCodeModel  =  projectItem.FileCodeModel

        
' 得到当前选定的内容
         Dim  selectText  As  TextSelection  =  DTE.ActiveDocument.Selection
        
' 获取到当前光标的位置
         Dim  point  As  TextPoint  =  selectText.ActivePoint

        Try
            
Dim  codeElement  As  CodeElement  =  fileCodeModel.CodeElementFromPoint(point, vsCMElement.vsCMElementVariable)

            
If  (codeElement  Is   Nothing Then
                Return
            
End   If

            Debug.Assert(codeElement.Kind 
=  vsCMElement.vsCMElementVariable)

            
Dim  codeVar  As  CodeVariable  =  CType(codeElement, CodeVariable)
            
Dim  fieldName  As   String   =  codeVar.Name
            
Dim  codeClass  As  CodeClass  =  CType(codeVar.Parent, CodeClass)

            AddPropertyToClass(codeClass, fieldName, codeVar.Type)
        Catch ex 
As  Exception
            
' 吃掉异常,不做处理或者提示
         End  Try

    
End Sub

    
Public   Sub  EncapsulateAllFields()
        
Dim  projectItem  As  ProjectItem  =  DTE.ActiveDocument.ProjectItem
        
Dim  fileCodeModel  As  FileCodeModel  =  projectItem.FileCodeModel

        Try
            
' 得到当前选定的内容
             Dim  selectText  As  TextSelection  =  DTE.ActiveDocument.Selection
            
' 获取到当前光标的位置
             Dim  point  As  TextPoint  =  selectText.ActivePoint

            
Dim  codeElement  As  CodeElement  =  fileCodeModel.CodeElementFromPoint(point, vsCMElement.vsCMElementClass)
            
Dim  codeClass  As  CodeClass  =  CType(codeElement, CodeClass)

            
Dim  i  As   Integer
            
For  i  =   1   To  codeClass.Members.Count
                
' 如果属性已经定义,会抛出异常
                 ' 在这里处理异常,即使新增的属性已经定义,也可以继续处理下面的代码
                Try
                    
Dim  element  As  CodeElement  =  codeClass.Members.Item(i)
                    
If  (element.Kind  =  vsCMElement.vsCMElementVariable)  Then
                        
Dim  codeVariable  As  CodeVariable  =  CType(element, CodeVariable)
                        
If  ( Not  codeVariable.IsShared)  Then       ' 静态变量不需要增加属性
                            AddPropertyToClass(codeClass, codeVariable.Name, codeVariable.Type)
                        
End   If
                    
End   If
                Catch ex 
As  Exception
                    
' 吃掉异常
                 End  Try
            
Next
        Catch ex 
As  Exception
            
' 可能并没有选择有效的类定义,这时会抛出异常,忽略
         End  Try
    
End Sub

    
' 根据成员的名称的类型,在类对象中插入属性
     Private   Sub  AddPropertyToClass(ByVal codeClass  As  CodeClass, ByVal fieldName  As   String , ByVal fieldType  As   Object )
        
' 生成属性的名称,规则是首先字母大写。如果变量的开头为“_”,移除
         Dim  propertyName  As   String   =  fieldName
        
If  (propertyName.StartsWith( " _ " ))  Then
            propertyName 
=  propertyName.TrimStart( " _ " c)
        
End   If
        propertyName 
=  propertyName.Substring( 0 1 ).ToUpper()  &  propertyName.Substring( 1 )

        
' 创建属性对象
         ' -1表示代码插入到类的最下方
         ' vsCMAccess.vsCMAccessPublic表示为public
         Dim  codeProperty  As  CodeProperty  =  codeClass.AddProperty(propertyName, propertyName, fieldType,  - 1 , vsCMAccess.vsCMAccessPublic)
        
' Getter
         Dim  getter  As  CodeFunction  =  codeProperty.Getter
        
Dim  getterPoint  As  TextPoint  =  getter.GetStartPoint(vsCMPart.vsCMPartBody)
        
Dim  getterEditPoint  As  EditPoint  =  getterPoint.CreateEditPoint()
        getterEditPoint.Delete(getter.GetEndPoint(vsCMPart.vsCMPartBody))
        getterEditPoint.Insert(vbCrLf)      
' 插入回车符
        getterEditPoint.LineUp()
        getterEditPoint.Indent(, 
4 )          ' 缩进4个位置
        getterEditPoint.Insert( " return  "   &  fieldName  &   " ; " )
        
' Setter
         Dim  setter  As  CodeFunction  =  codeProperty.Setter
        
Dim  setterPoint  As  TextPoint  =  setter.GetStartPoint(vsCMPart.vsCMPartBody)
        
Dim  setterEditPoint  As  EditPoint  =  setterPoint.CreateEditPoint()
        setterEditPoint.Insert(vbCrLf)     
' 插入回车符
        setterEditPoint.LineUp()
        setterEditPoint.Indent(, 
4 )          ' 缩进4个位置
        setterEditPoint.Insert(fieldName  &   "  = value; " )
    
End Sub
End  Module


我定义了两个Public方法:EncapsulateField和EncapsulateAllFields,分别用于为类的一个变量封装属性,或者为类中所有的变量(非静态)封装属性。

使用上面的宏的方法很简单,选择“工具”>“宏”>“宏资源管理器”就可以看到我们已经创建的宏方法,如下图所示:


假如你已经编写了这样一段代码:
using  System;
using  System.Collections.Generic;
using  System.Text;

namespace  Demo
{
    
public   class  Person
    {
        
private   int  _id;
        
private   string  _name;
        
private  DateTime _birthDay;
    }
}

将光标移到“_name”变量上,然后双击“EncapsulateField”宏,就运行了该宏。运行后,你可以得到这样的代码:
using  System;
using  System.Collections.Generic;
using  System.Text;

namespace  Demo
{
    
public   class  Person
    {
        
private   int  _id;
        
private   string  _name;
        
private  DateTime _birthDay;

        
public   string  Name
        {
            
get
            {
                
return  _name;
            }
            
set
            {
                _name 
=  value;
            }
        }
    }
}

可以看到“EncapsulateField”宏已经为private string _name;创建了相应的属性。
EncapsulateAllFields宏只需要将光标放在Person类的代码区域中,就可以正常执行。例如针对上面的代码,EncapsulateAllFields后可以为Person类中的每一个变量都生成相应的属性。(注:上面的代码中Name属性已经有定义,所有试图再添加Name属性时会抛出异常,在EncapsulateAllFields宏定义中,已经将该异常吃掉,所以,可以正确地为所有变量生成属性)。运行后的代码如下:
using  System;
using  System.Collections.Generic;
using  System.Text;

namespace  Demo
{
    
public   class  Person
    {
        
private   int  _id;
        
private   string  _name;
        
private  DateTime _birthDay;

        
public   string  Name
        {
            
get
            {
                
return  _name;
            }
            
set
            {
                _name 
=  value;
            }
        }

        
public   int  Id
        {
            
get
            {
                
return  _id;
            }
            
set
            {
                _id 
=  value;
            }
        }

        
public  System.DateTime BirthDay
        {
            
get
            {
                
return  _birthDay;
            }
            
set
            {
                _birthDay 
=  value;
            }
        }
    }
}


在确认上面的宏可以正确运行的情况下,我们还可以为其定义快捷键,进一步提高我们的工作效率。
在Visual Studio 2005 IDE中选择“工具” > “选项”

在“选项”对话框中选择“环境”>“键盘”。
在【显示命令包含】中输入Encap...,可以帮你快速定位到相应的宏命令;
【新快捷键用于】选项中选择“文本编辑器”;
激活【按快捷键】输入框,选择你所希望的快捷键,例如我这里为EncapsulateField定义的快捷键为“Ctrl + `”,为EncapsulateAllFields定义的快捷键为“Ctrl + Shift + `”
点击分配按钮

你就可以在文本编辑器中尽情享用宏给你带来的方便。


补充在Visual Studio 2003中使用的宏:
Visual Studio 2003 IDE与Visual Studio 2005 IDE在处理上稍微有些不一致的地方,需要做如下的调整:
Imports System
Imports EnvDTE
Imports System.Diagnostics

Public  Module EditorHelper
    
' 为一个参数封装一般属性访问器
     Public   Sub  EncapsulateField()
        
Dim  projectItem  As  ProjectItem  =  DTE.ActiveDocument.ProjectItem
        
Dim  fileCodeModel  As  FileCodeModel  =  projectItem.FileCodeModel

        
' 得到当前选定的内容
         Dim  selectText  As  TextSelection  =  DTE.ActiveDocument.Selection
        
' 获取到当前光标的位置
         Dim  point  As  TextPoint  =  selectText.ActivePoint

        Try
            
Dim  codeElement  As  CodeElement  =  fileCodeModel.CodeElementFromPoint(point, vsCMElement.vsCMElementVariable)

            
If  (codeElement  Is   Nothing Then
                Return
            
End   If

            Debug.Assert(codeElement.Kind 
=  vsCMElement.vsCMElementVariable)

            
Dim  codeVar  As  CodeVariable  =  CType(codeElement, CodeVariable)
            
Dim  fieldName  As   String   =  codeVar.Name
            
Dim  codeClass  As  CodeClass  =  CType(codeVar.Parent, CodeClass)

            AddPropertyToClass(codeClass, fieldName, codeVar.Type)
        Catch ex 
As  Exception
            
' 吃掉异常,不做处理或者提示
             MsgBox (ex.Message)
        
End  Try

    
End Sub

    
Public   Sub  EncapsulateAllFields()
        
Dim  projectItem  As  ProjectItem  =  DTE.ActiveDocument.ProjectItem
        
Dim  fileCodeModel  As  FileCodeModel  =  projectItem.FileCodeModel

        Try
            
' 得到当前选定的内容
             Dim  selectText  As  TextSelection  =  DTE.ActiveDocument.Selection
            
' 获取到当前光标的位置
             Dim  point  As  TextPoint  =  selectText.ActivePoint

            
Dim  codeElement  As  CodeElement  =  fileCodeModel.CodeElementFromPoint(point, vsCMElement.vsCMElementClass)
            
Dim  codeClass  As  CodeClass  =  CType(codeElement, CodeClass)

            
Dim  i  As   Integer
            
For  i  =   1   To  codeClass.Members.Count
                
' 如果属性已经定义,会抛出异常
                 ' 在这里处理异常,即使新增的属性已经定义,也可以继续处理下面的代码
                Try
                    
Dim  element  As  CodeElement  =  codeClass.Members.Item(i)
                    
If  (element.Kind  =  vsCMElement.vsCMElementVariable)  Then
                        
Dim  codeVariable  As  CodeVariable  =  CType(element, CodeVariable)
                        
If  ( Not  codeVariable.IsShared)  Then       ' 静态变量不需要增加属性
                            AddPropertyToClass(codeClass, codeVariable.Name, codeVariable.Type)
                        
End   If
                    
End   If
                Catch ex 
As  Exception
                    
' 吃掉异常
                 End  Try
            
Next
        Catch ex 
As  Exception
            
' 可能并没有选择有效的类定义,这时会抛出异常,忽略
             MsgBox (ex.Message)
        
End  Try
    
End Sub

    
' 根据成员的名称的类型,在类对象中插入属性
     Private   Sub  AddPropertyToClass(ByVal codeClass  As  CodeClass, ByVal fieldName  As   String , ByVal fieldType  As   Object )
        
' 生成属性的名称,规则是首先字母大写。如果变量的开头为“_”,移除
         Dim  propertyName  As   String   =  fieldName
        
If  (propertyName.StartsWith( " _ " ))  Then
            propertyName 
=  propertyName.TrimStart( " _ " c)
        
End   If
        propertyName 
=  propertyName.Substring( 0 1 ).ToUpper()  &  propertyName.Substring( 1 )

        
' 创建属性对象
         ' -1表示代码插入到类的最下方
         ' vsCMAccess.vsCMAccessPublic表示为public
         Dim  codeProperty  As  CodeProperty  =  codeClass.AddProperty(propertyName, propertyName, fieldType,  - 1 , vsCMAccess.vsCMAccessPublic)
        
' Getter
         Dim  getter  As  CodeFunction  =  codeProperty.Getter
        
Dim  getterPoint  As  TextPoint  =  getter.GetStartPoint(vsCMPart.vsCMPartBody)
        
Dim  getterEditPoint  As  EditPoint  =  getterPoint.CreateEditPoint()
        getterEditPoint.Delete(getter.GetEndPoint(vsCMPart.vsCMPartBody))
        getterEditPoint.Insert(
" get{ return  "   &  fieldName  &   " ; } " )
        
' Setter
         Dim  setter  As  CodeFunction  =  codeProperty.Setter
        
Dim  setterPoint  As  TextPoint  =  setter.GetStartPoint(vsCMPart.vsCMPartBody)
        
Dim  setterEditPoint  As  EditPoint  =  setterPoint.CreateEditPoint()
        setterEditPoint.Delete(setter.GetEndPoint(vsCMPart.vsCMPartBody))
        setterEditPoint.Insert(
" set{  "   &  fieldName  &   "  = value; } " )
    
End Sub
End  Module


另外,在2003中使用EncapsulateAllFields的宏时,也需要注意一点:在使用时,光标的应该停留在类定义的空白位置,否则使用Dim codeClass As CodeClass = CType(codeElement, CodeClass)方法不能正确获取到类对象(例如,如果光标在构造函数里,获取到的是构造函数对象,这点与2005还是有所区别的)。


参考:
《用Visual Studio 2005创建宏代码生成器》:转载太多,已经不知道原出处

转载于:https://www.cnblogs.com/scdsun/archive/2007/03/15/675946.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值