Практическое руководство. Поиск кода при помощи модели кода (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, можно выбрать либо QueryInterface (QI) для CodeClass из модели кода Visual Studio, либо QI для VCCodeClass из модели кода, зависящей от языка Visual C++. Дополнительные сведения о модели кода, зависящей от языка Visual C++ см. в разделах Практическое руководство. Управление кодом при помощи модели кода Visual C++ (Visual C#) и модель кода Visual C++.
Замечания о модели кода Visual Studio
Только реализация модели кода Visual C++ выполняет зависящее от языка моделирование для реализации языка от Microsoft.
В некоторых языках не реализуется полная модель кода Visual Studio. Исключения, если они есть, указаны в справке. Большинство различий между реализациями модели кода вызвано функциональными различиями языков. Например, нельзя добавить функции к объектам CodeNamespace в Visual Basic или Visual C#, поскольку определять функции на верхнем уровне позволяет только 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, а затем свойству Name CodeFunction задается значение "YourFunction". После такого присвоения переменной уже нельзя гарантировать, что локальная переменная представляет ту же CodeFunction. Следовательно, обращение к значению cвойства может в результате привести к 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 C++ (Visual Basic)
Основные понятия
Практическое руководство. Поиск кода при помощи модели кода (Visual Basic)
Диаграмма модели объектов автоматизации