使用代码模型查找代码 (Visual C#)
Visual Studio 代码模型为自动化客户端提供了查找项目中的代码定义和修改这些代码元素的能力。 当在代码编辑器中进行修改后,代码模型自动更新所有引用的对象。 例如,如果引用某个类对象并且稍后用户添加一个新函数,则在成员中列出此对象。 代码模型允许自动化客户端不必实现 Visual Studio 语言的分析器,就可以发现项目中的高级定义(如类、接口、结构、方法、属性等)。
Visual Studio 核心代码模型避免了涉及代码的语言特定部分,因此它不为函数中的语句提供对象模型,也不提供有关参数的所有详细信息。 对于参数,代码模型仅公开参数的类型和名称,而不提供关于此参数是输入、输出还是可选等信息。 Visual C++ 提供核心代码模型的扩展版本,该版本专门针对 Visual C++ 项目。 有关此操作的信息,请参见 Visual C++ 代码模型。
用代码模型检查和编辑代码
代码模型主要基于文本,因为项目中的程序或代码都存储在文本文件中。 可以使用项目模型来查找项目的代码以访问每个项目项,然后通过使用 FileCodeModel 检查项目项是否包含代码。 如果项目项包含代码元素,则这些元素可以从编辑器返回对象,并且代码模型可以使用文本编辑器自动化模型来修改代码或执行本地化分析。 使用编辑器对象模型,可以请求包含编辑器的插入点的代码元素,或包含函数或类级别的 TextPoint 对象的代码元素。
Visual Studio 核心代码模型的主要入口点为 CodeModel 对象。 常规 CodeElements 集合被用在代码模型的若干位置中。 有一个位于 CodeElements 级别和类或接口级别,它返回这些对象的成员。 CodeElements 集合的每个元素都是 CodeElement2 对象,并且每个 CodeElement2 对象都有标识其类型(无论它是类、接口、结构、函数、属性、变量等)的 Kind 属性。
语言特定的代码模型
Visual C++ 为核心代码模型提供一个扩展,以便面向特定于 Visual C++ 的代码。 例如,如果 Language 指示给定的代码元素为 Visual C++ 代码模型对象,而且 Kind = vsCMElementClass,则可以从 Visual Studio 代码模型中选择 CodeClass 的 QueryInterface (QI),或从 Visual C++ 语言特定代码模型中选择 VCCodeClass 的 QI。 有关特定于 Visual C++ 的代码模型的更多信息,请参见如何:使用 Visual C++ 代码模型操作代码 (Visual C#) 和 Visual C++ 代码模型。
关于 Visual Studio 代码模型的说明
只有 Visual C++ 代码模型实现执行 Microsoft 语言实现的语言特定建模。
有些语言不实现整个 Visual Studio 代码模型。 存在异常时,帮助主题会指出这些异常。 代码模型的实现之间的大多数差异是由于语言间的功能差异引起的。 例如,无法将函数添加到 Visual Basic 或 Visual C# 中的 CodeNamespace 对象,因为只有 Visual C++ 提供了顶级函数定义。
说明
此外接程序演练 Visual Studio 文件的代码元素。 为了运行此示例,必须在 Visual Studio 代码编辑器中打开代码文件。 有关如何运行这些示例的更多信息,请参见如何:编译和运行自动化对象模型代码示例。
代码
// Add-in code.
using System.Windows.Forms;
public void OnConnection(object application,
Extensibility.ext_ConnectMode connectMode, object addInInst, ref
System.Array custom)
{
_applicationObject = (_DTE2)application;
_addInInstance = (AddIn)addInInst;
// Pass the applicationObject member variable to the code example.
OutlineCode((DTE2)_applicationObject);
}
public void OutlineCode( DTE2 dte )
{
FileCodeModel fileCM =
dte.ActiveDocument.ProjectItem.FileCodeModel;
CodeElements elts = null;
elts = fileCM.CodeElements;
CodeElement elt = null;
int i = 0;
MessageBox.Show( "about to walk top-level code elements ...");
for ( i=1; i<=fileCM.CodeElements.Count; i++ )
{
elt = elts.Item( i );
CollapseElt( elt, elts, i );
}
}
public void CollapseElt( CodeElement elt, CodeElements elts, long loc )
{
EditPoint epStart = null;
EditPoint epEnd = null;
epStart = elt.StartPoint.CreateEditPoint();
// Do this because we move it later.
epEnd = elt.EndPoint.CreateEditPoint();
epStart.EndOfLine();
if ( ( ( elt.IsCodeType ) & ( elt.Kind !=
vsCMElement.vsCMElementDelegate ) ) )
{
MessageBox.Show( "got type but not a delegate,
named : " + elt.Name);
CodeType ct = null;
ct = ( ( EnvDTE.CodeType )( elt ) );
CodeElements mems = null;
mems = ct.Members;
int i = 0;
for ( i=1; i<=ct.Members.Count; i++ )
{
CollapseElt( mems.Item( i ), mems, i );
}
}
else if ( ( elt.Kind == vsCMElement.vsCMElementNamespace ) )
{
MessageBox.Show( "got a namespace, named: " + elt.Name);
CodeNamespace cns = null;
cns = ( ( EnvDTE.CodeNamespace )( elt ) );
MessageBox.Show( "set cns = elt, named: " + cns.Name);
CodeElements mems_vb = null;
mems_vb = cns.Members;
MessageBox.Show( "got cns.members");
int i = 0;
for ( i=1; i<=cns.Members.Count; i++ )
{
CollapseElt( mems_vb.Item( i ), mems_vb, i );
}
}
}
代码模型元素值可更改
代码模型元素的指定值(如类、构造、函数、特性、委托等)在进行某些编辑后可能更改。 因此,您无法假定值将保持不变。
例如,若您为某个局部变量指定一个代码模型元素,随后为此局部变量设置属性值,则您随后引用此局部变量时,该局部变量可能不包含有效的代码模型元素。 实际上,它甚至可能包含一个不同的代码模型元素。
请考虑一个包含名为“MyFunction”的函数的类,该函数被赋给 CodeFunction 变量,随后 CodeFunction 的 Name 属性设置为值“YourFunction”。此变量赋值后,将不再保证您的局部变量代表的是同一个 CodeFunction。 随后,访问此属性的值可能返回 E_FAIL 作为结果。
建议处理此情况的做法是在访问此局部变量的属性值之前,显式地将此局部变量重新指定为正确的代码模型元素。 下面将演示如何执行此操作的一个示例。 (代码是以外接程序的形式存在的。)
说明
此外接程序演示了访问 CodeElements 的值的正确方法,使得可以检索正确的值。 有关如何运行这些示例的更多信息,请参见如何:编译和运行自动化对象模型代码示例。
代码
[Visual Basic]
Public Sub OnConnection(ByVal application As Object, ByVal _
connectMode As ext_ConnectMode, ByVal addInInst As Object, _
ByRef custom As Array) Implements IDTExtensibility2.OnConnection
_applicationObject = CType(application, DTE2)
_addInInstance = CType(addInInst, AddIn)
ReassignValue(_applicationObject)
End Sub
Sub ReassignValue(ByVal dte As DTE2)
' Before running, create a new Windows application project,
' and then add a function to it named MyFunction.
Try
Dim myFCM As FileCodeModel = _
dte.ActiveDocument.ProjectItem.FileCodeModel
' Change the MyFunction name in Form1 class to
' the name, OtherFunction.
Dim myClass1 As CodeClass = _
CType(myFCM.CodeElements.Item("Form1"), CodeClass2)
Dim myFunction As CodeFunction = _
CType(myClass1.Members.Item("MyFunction"), CodeFunction2)
myFunction.Name = "OtherFunction"
myFunction = CType(myClass1.Members.Item("OtherFunction"), _
CodeFunction2)
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
[C#]
public void OnConnection(object application, ext_ConnectMode
connectMode, object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
ReassignValue(_applicationObject);
}
// Before running, create a new Windows application project,
// and then add a function to it named MyFunction.
public void ReassignValue(DTE2 dte)
{
try
{
FileCodeModel myFCM =
dte.ActiveDocument.ProjectItem.FileCodeModel;
// Change the MyFunction name in Form1 class to
// the name, OtherFunction.
CodeClass myClass1 =
(CodeClass2)myFCM.CodeElements.Item("Form1");
CodeFunction myFunction =
(CodeFunction2)myClass1.Members.Item("MyFunction");
myFunction.Name = "OtherFunction";
myFunction =
(CodeFunction2)myClass1.Members.Item("OtherFunction");
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
提示
设置代码模型元素的子元素的属性并不会展示此行为。 只有直接影响 CodeElement 的属性(如元素名、函数类型、方法签名等) 才能展示此不确定的行为。
同时,仅当 CodeElement 的新名在其同级中独一无二的情况下此示例才能运行。 这是因为 Item 属性返回的第一个匹配项对同名的重载方法/属性、分部类或命名空间无效。
请参见
任务
如何:使用 Visual C++ 代码模型操作代码 (Visual C#)
如何:使用 Visual C++ 代码模型操作代码 (Visual Basic)