コード モデルを使用したコードの調査 (Visual C#)
更新 : 2007 年 11 月
Visual Studio コード モデルには、オートメーション クライアントでプロジェクト内のコード定義を調査し、コード要素を変更する機能が用意されています。コード エディタで内容が変更された場合、コード モデルによって、参照されているすべてのオブジェクトが自動的に更新されます。たとえば、クラス オブジェクトを参照している場合に、別のユーザーが新しい関数を追加すると、その追加された関数はメンバの中に表示されます。コード モデルを使用すると、オートメーション クライアントでは、プロジェクト内でクラス、インターフェイス、構造体、メソッド、プロパティなどの高度な定義を調査するために、Visual Studio 言語のパーサーを実装する必要がなくなります。
Visual Studio コア コード モデルでは、コードの言語固有の部分はありません。たとえば、関数内のステートメント用のオブジェクト モデルは用意されておらず、パラメータの詳細も指定されていません。パラメータに関しては、コード モデルでは、パラメータの型と名前だけが公開されます。パラメータが入力パラメータ、出力パラメータ、省略可能なパラメータなどのうちどれであるかを示す情報は提供されません。Visual C++ には、Visual C++ プロジェクトを対象とするコア コード モデルの拡張バージョンが用意されています。マクロのデバッグについては、「Visual C++ コード モデル」を参照してください。
コード モデルによるコードの調査と編集
コード モデルは、主にテキスト ベースであり、プロジェクトのプログラムやコードはテキスト ファイルに格納されています。プロジェクト モデルを使用してプロジェクトのコード内を検索し、各プロジェクト項目を参照します。その後、FileCodeModel を使用して、プロジェクト項目にコードが含まれているかどうかをチェックします。プロジェクト項目にコード要素が含まれる場合、コード要素はエディタからオブジェクトを返すことができます。また、コード モデルでは、テキスト エディタのオートメーション モデルを使用して、コードを変更したりローカライズされた解析を実行したりできます。Editor オブジェクト モデルを使用すると、エディタのカーソル位置を含むコード要素、または関数レベルやクラス レベルの 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 コード モデルに関するメモ
言語に固有な Microsoft 言語実装のモデリングを実行するのは、Visual C++ コード モデルの実装のみです。
一部の言語では、Visual Studio コード モデル全体が実装されません。ヘルプ トピックは、例外が存在する場合にその例外を示します。コード モデルの実装間の相違点の多くは、各言語で機能が異なることが原因です。たとえば、トップレベルの関数定義は Visual C++ でしか機能しないため、Visual Basic または Visual C# では、CodeNamespace オブジェクトに関数を追加できません。
説明
このアドインでは、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 );
}
}
}
変更されるコード モデル要素値
なんらかの編集よって、クラス、構造体、関数、属性、デリゲートなどのコード モデル要素に割り当てられた値が変更されることがあります。したがって、これらの値が静的なものであると想定することはできません。
たとえば、コード モデル要素をローカル変数に割り当て、このローカル変数のプロパティ値を設定した場合、あとでローカル変数を参照すると有効なコード モデル要素が含まれていないことがあります。実際、別のコード モデル要素が含まれていることもあります。
CodeFunction 変数に割り当て、CodeFunction の Name プロパティに "YourFunction" の値を設定した "MyFunction" という名前の関数を含むクラスの例を考えてみます。このように変数を割り当てると、ローカル変数が同じ 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 C++ コード モデルを使用したコード操作 (Visual Basic)
概念
コード モデルを使用したコードの調査 (Visual Basic)