通过PropertyManager Add-In管理代码中的属性

转帖|其它|编辑:郝浩|2009-04-02 09:57:51.000|阅读 509 次

概述:C#3.0引入了自动属性的特性,使得我们在处理简单属性的时候更为简单。但是我们有时候还是需要使用传统风格的属性声明方式,PropertyManager还要能在这两种声明方式之间进行转化。

# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>

前言

本文将介绍另一个与编辑器相关的例子PropertyManager。

问题分析

PropertyManager用来管理代码中的属性。我们在C# 3.0之前编写代码时,与属性相关的主要操作是将字段封装为属性,VS 2005中的Refactor->Encapsulate Field菜单(快捷键Ctrl+R, E)可以简化这个操作。它每次封装一个字段,并将生成的属性放在字段所在行的下一行,但是大部分人的编码习惯是将字段放在一起,将属性们放在一起,所以首先PropertyManager要做到这一点。

C#3.0引入了自动属性的特性,使得我们在处理简单属性的时候更为简单。但是我们有时候还是需要使用传统风格的属性声明方式,PropertyManager还要能在这两种声明方式之间进行转化。

下面就来看看如何在Add-In中实现这些功能。

实现PropertyManager

1)添加命令

按上面的分析,这里需要三个命令,界面看起来像这样:

property-manager-menu

Batch Property为选中的多个字段生成属性代码。这里将原有的传统的声明方式称为Normal-Property,将自动属性称为Auto-Property,后面的两个命令即是在这两种方式间进行转换。

使用下面的代码添加命令: 

C# Code - 添加ProjectManager命令
CommandBar codeWinCommandBar = helper.GetCommandBarByName("Code Window");

int pmPopupIndex = codeWinCommandBar.Controls.Count + 1;
CommandBarPopup pmPopup
= codeWinCommandBar.Controls.Add(
    MsoControlType.msoControlPopup, Type.Missing, Type.Missing,
    pmPopupIndex,
true) as CommandBarPopup;
pmPopup.Caption
= "PropertyManager";

CommandBarButton batchPropertyCmd
= helper.AddButtonToPopup
(pmPopup, pmPopup.Controls.Count
+ 1,
   
"Batch Property", "Encapsulate these fields");
batchPropertyCmdEvent
= _applicationObject.Events.get_
CommandBarEvents(batchPropertyCmd)
as CommandBarEvents;
batchPropertyCmdEvent.Click
+= new _dispCommandBarControlEvents_
ClickEventHandler(BatchPropertyCmdEvent_Click);

CommandBarButton convertToAutoPropCmd
= helper.
AddButtonToPopup(pmPopup, pmPopup.Controls.Count
+ 1,
   
"Convert to Auto-Property", "Convert to Auto-Property(C# 3.0 style)");
convertToAutoPropCmdEvent
= _applicationObject.Events.get_
CommandBarEvents(convertToAutoPropCmd)
as CommandBarEvents;
convertToAutoPropCmdEvent.Click
+= new _dispCommandBarControlEvents_
ClickEventHandler(ConvertToAutoPropCmdEvent_Click);

CommandBarButton convertToNormalPropCmd
=
 helper.AddButtonToPopup(pmPopup, pmPopup.Controls.Count + 1,
   
"Convert to Normal-Property", "Convert to Normal-Property
(C# 2.0 style)
");
convertToNormalPropCmdEvent
= _applicationObject.Events.get_
CommandBarEvents(convertToNormalPropCmd)
as CommandBarEvents;
convertToNormalPropCmdEvent.Click
+= new _dispCommandBarControlEvents_
ClickEventHandler(ConvertToNormalPropCmdEvent_Click);


2)命令的实现

为简化问题,这里约定字段、自动属性和常规属性的声明格式为:

C# Code - 字段和属性约定格式
private int id;
string name;
private DateTime birth;

private int Id { get; set; }
public string Name { private get; set; }
public DateTime Birth { get; private set; }

public int Id
{
   
get { return id; }
   
set { id = value; }
}
public string Name
{
   
get { return name; }
   
set { name = value; }
}
public DateTime Birth
{
   
get { return birth; }
   
set { birth = value; }
}


字段采用camelCase风格命名,属性采用PascalCase风格命名。

在实现Batch Property时,可以采用正则表达式找出选中行中的多个字段,为它们一一生成属性,最后将属性代码放入剪贴板:


private void BatchPropertyCmdEvent_Click(object CommandBarControl, 
ref bool Handled, ref bool CancelDefault)
{
   
string selectedLines = helper.GetSelectedLines();
    GenerateProperties(selectedLines);
}

private void GenerateProperties(string selectedLines)
{
    Regex regex
= new Regex(".*(?<type>\\b\\w+\\b)\\s+(?<fieldName>\\w+)(\\s*=.+)*;", RegexOptions.IgnoreCase);
    MatchCollection fieldLines
= regex.Matches(selectedLines);

   
// 约定字段为camelCase, 属性为PascalCase
    StringBuilder props = new StringBuilder();
   
foreach (Match fieldLine in fieldLines)
    {
       
string type = fieldLine.Groups["type"].Value;
       
string fieldName = fieldLine.Groups["fieldName"].Value;
       
bool readOnly = fieldLine.Value.IndexOf("readonly") >= 0;
        props.AppendLine(GenerateNormalProperty
(
"public", type, fieldName, readOnly, false));
    }

    Clipboard.SetText(props.ToString());
}

private string GenerateNormalProperty
(
string modifier, string type, string fieldName, bool readOnly, bool writeOnly)
{
   
string propertyName = StringHelper.GetPascalCaseString(fieldName);
   
// 添加一个换行可利用VS的自动格式特性
    string format = "{0} {1} {2}{{ " + Environment.NewLine + "{3} {4} }}";
   
   
string getter = string.Empty;
   
if (!writeOnly)
    {
        getter
= string.Format("get{{ return {0}; }}", fieldName);
    }

   
string setter = string.Empty;
   
if (!readOnly)
    {
        setter
= string.Format("set {{ {0} = value; }}", fieldName);
    }

   
return string.Format(format, modifier, type, propertyName, getter, setter);
}


GetSelectedLines方法是获取当前文档中选中的代码行:


public string GetSelectedLines()
{
    TextSelection selectedText
= dte.ActiveDocument.Selection as TextSelection;
    TextPoint topPoint
= selectedText.TopPoint;
    EditPoint bottomPoint
= selectedText.BottomPoint.CreateEditPoint();

   
return bottomPoint.GetLines(topPoint.Line, bottomPoint.Line + 1);
}


字段可能为readonly的,此时将只生成getter代码。这样在生成属性时考虑的主要因素为:字段类型、字段名称、是否只读。这样

C# Code
private int id;
string name;
private readonly DateTime birth;


会生成如下的代码(会拷贝到剪贴板中):

C# Code
public int Id
{
   
get { return id; }
   
set { id = value; }
}
public string Name
{
   
get { return name; }
   
set { name = value; }
}
public DateTime Birth
{
   
get { return birth; }
}


在实现Convert to Auto-Property时,思路与上面相同,也是将查找到的各个常规属性转换为自动属性:


private void ConvertToAutoPropCmdEvent_Click(object CommandBarControl, ref bool Handled, ref bool CancelDefault)
{
   
string selectedLines = helper.GetSelectedLines();
    Regex propRegex
= new Regex(
       
"((?<modifier>\\b\\w+\\b)\\s+)*(?<type>\\b\\w+\\b)\\s+(?<name>\\w+)\\s*{(.|\\n)+?(?<!;\\s*)}",
        RegexOptions.IgnoreCase
| RegexOptions.Multiline);
    MatchCollection properties
= propRegex.Matches(selectedLines);

    StringBuilder autoProps
= new StringBuilder();
   
foreach (Match prop in properties)
    {
       
string modifier = prop.Groups["modifier"].Value;
       
if (modifier.Length == 0)
        {
            modifier
= "private";
        }
       
string type = prop.Groups["type"].Value;
       
string name = prop.Groups["name"].Value;

       
string propText = prop.Value;
       
bool hasGetter = Regex.IsMatch(propText, "get\\s*{(.|\\n)+?}");
       
bool hasSetter = Regex.IsMatch(propText, "set\\s*{(.|\\n)+?}");
       
if (!hasGetter && !hasSetter) { break; }

        autoProps.AppendLine(GenerateAutoProperty(modifier, type, name, hasGetter, hasSetter));
    }

    Clipboard.SetText(autoProps.ToString());
}

private string GenerateAutoProperty(string modifier, string type, string name, bool hasGetter, bool hasSetter)
{
   
string format = "{0} {1} {2}{{ {3} {4} }}";
   
string getter = "get;";
   
if (!hasGetter)
    {
        getter
= "private " + getter;
    }

   
string setter = "set;";
   
if (!hasSetter)
    {
        setter
= "private " + setter;
    }

   
return string.Format(format, modifier, type, name, getter, setter);
}


对于代码

public int Id
{
   
get { return id; }
   
set { id = value; }
}
public string Name
{
   
set { name = value; }
}
public DateTime Birth
{
   
get { return birth; }
}


生成的结果为:

public int Id { get; set; }
public string Name { private get; set; }
public DateTime Birth { get; private set; }


最后是Convert to Normal-Property

C# Code - 实现Convert to Normal-Property命令
private void ConvertToNormalPropCmdEvent_Click(object CommandBarControl, ref bool Handled, ref bool CancelDefault)
{
   
string selectedLines = helper.GetSelectedLines();
    Regex propRegex
= new Regex(
       
"((?<modifier>\\w+)?\\s+)?(?<type>\\w+)\\s+(?<name>\\w+)\\s*{(.|\\n)*?get;(.|\\n)*?set;\\s*}",
        RegexOptions.IgnoreCase
| RegexOptions.Multiline);
    MatchCollection autoProperties
= propRegex.Matches(selectedLines);

    StringBuilder normalProps
= new StringBuilder();
   
foreach (Match prop in autoProperties)
    {
       
string modifier = prop.Groups["modifier"].Value;
       
string type = prop.Groups["type"].Value;
       
string name = prop.Groups["name"].Value;

       
string propText = prop.Value;
       
bool writeOnly = Regex.IsMatch(propText, "private\\s+get;");
       
bool readOnly = Regex.IsMatch(propText, "private\\s+set;");
       
if (writeOnly && readOnly) { break; }

        normalProps.AppendLine(GenerateNormalProperty(modifier, type, name, readOnly, writeOnly));
    }

    Clipboard.SetText(normalProps.ToString());
}


至此这三个命令就全部实现了,它们都是将结果放在剪切板中,因为我感觉很难确定出一个合适的位置。

可以从这里下载代码,也可以在这里下载可运行的Add-In(解压缩后将文件放在[My Documents Path]\Visual Studio 2008\Addins下)。

希望这个功能能让您对编辑器的扩展有更多的了解。


标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com

文章转载自:博客园

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
扫码咨询


添加微信 立即咨询

电话咨询

客服热线
023-68661681

TOP