共用方式為


使用程式碼模型探索程式碼 (Visual C#)

Visual Studio 程式碼模型為 Automation 用戶端提供了探索專案中之程式碼定義,以及修改這些程式碼項目的功能。 程式碼模型會在程式碼編輯器進行修改時,自動更新所有參考的物件。 例如,如果您是參考類別物件,而且使用者後來加入了新的函式,便會將其列在成員中。 程式碼模型讓 Automation 用戶端不必實作 Visual Studio 語言的剖析器 (Parser),就能探索專案中的高階定義,例如類別、介面、結構、方法、屬性等。

Visual Studio 核心程式碼模型會避免使用程式碼的語言特定區域,使其不會為函式中的陳述式提供物件模型,或是提供參數的完整詳細資料。 以參數而言,程式碼模型只會公開參數的型別和名稱,但是不會提供參數是否為輸入、輸出、選擇性等資訊。 Visual C++ 則另外針對 Visual C++ 專案提供擴充版的核心程式碼模型。 如需更多資訊,請參閱 Visual C++ 程式碼模型

使用程式碼模型檢查及編輯程式碼

程式碼模型主要是文字式,因為專案中的程式或程式碼都儲存在文字檔中。 若要尋找專案的程式碼,可以使用專案模型瀏覽每一個專案項目,然後使用 FileCodeModel 查看專案項目是否包含程式碼。 如果專案項目包含程式碼項目,這些項目可以從編輯器傳回物件,而且程式碼模型可以使用文字編輯器的 Automation 模型修改程式碼或執行當地語系化剖析。 使用編輯器物件模型,可以要求包含編輯器插入點的程式碼項目,或是函式或類別層級的 TextPoint 物件。

Visual Studio 核心程式碼模型的主要進入點 (Entry Point) 是 CodeModel 物件。 程式碼模型中有許多地方都會用到一般的 CodeElements 集合。 在 CodeElements 層級,以及傳回這些物件之成員的類別或介面層級就有一處。 CodeElements 集合中的每一個項目都是 CodeElement2 物件,而每一個 CodeElement2 物件都具有 Kind 屬性,用來識別其類型為類別、介面、結構 (Struct)、函式、屬性或變數等。

語言特定程式碼模型

Visual C++ 針對 Visual C++ 專屬的程式碼,為核心程式碼模型提供了擴充功能。 例如,如果 Language 指出某個程式碼項目為 Visual C++ 程式碼模型物件,而且 Kind = vsCMElementClass,則您可以從 Visual Studio 程式碼模型中為 CodeClass 選擇 QueryInterface (QI),或是從 Visual C++ 語言特定程式碼模型中為 VCCodeClass 選擇 QI。 如需 Visual C++ 專屬程式碼模型的詳細資訊,請參閱 HOW TO:使用 Visual C++ 程式碼模型管理程式碼 (Visual C#)Visual C++ 程式碼模型

Visual Studio 程式碼模型的注意事項

  • 只有 Visual C++ 程式碼模型實作 (Implementation) 會執行 Microsoft 語言實作的特定語言模型。

  • 有些語言並不會實作整個 Visual Studio 程式碼模型。 [說明] 主題指出所發生的例外狀況。 程式碼模型實作之間的大多數差異都是因為不同語言的功能差異所導致的。 例如,您不能在 Visual Basic 或 Visual C# 的 CodeNamespace 物件中加入函式,因為只有 Visual C++ 才有提供最上層的函式定義。

描述

這個增益集 (Add-In) 會逐步執行 Visual Studio 檔案中的程式碼項目。 若要執行這個範例,必須在 Visual Studio 程式碼編輯器中開啟程式碼檔案。 如需如何執行範例的詳細資訊,請參閱 HOW TO:編譯和執行 Automation 物件模型程式碼範例

程式碼

// 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 屬性 (Property) 接著也將值設為 "YourFunction"。那麼在這個變數指派後,您將無法再保證這一個區域變數一定表示相同的 CodeFunction。 所以,存取該屬性的值可能會導致傳回 E_FAIL。

處理這個情況的建議作法是在存取這個區域變數的屬性值之前,先將區域變數明確地重新指派給正確的程式碼模式項目。 下列範例會示範這個作法 (範例程式碼是以增益集的方式表現)。

描述

這個增益集示範了如何正確存取 CodeElements 的值,才能擷取到適當的值。 如需如何執行範例的詳細資訊,請參閱 HOW TO:編譯和執行 Automation 物件模型程式碼範例

程式碼

[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 屬性會傳回第一筆相符的項目,而該筆項目不適用於多載的方法/屬性、部分類別或具有相同名稱的命名空間。

請參閱

工作

HOW TO:編譯 Visual C++ 程式碼模型擴充性的範例程式碼

HOW TO:建立增益集

逐步解說:建立精靈

HOW TO:使用 Visual C++ 程式碼模型管理程式碼 (Visual C#)

HOW TO:使用 Visual C++ 程式碼模型管理程式碼 (Visual Basic)

概念

使用程式碼模型探索程式碼 (Visual Basic)

Visual C++ 程式碼模型

Automation 物件模型圖表

其他資源

建立和控制環境視窗

建立增益集和精靈

Automation 與擴充性參考