2015 年 10 月

第 30 卷,第 10 期

本文章是由機器翻譯。

程式碼分析 - 使用整合式 Roslyn 程式碼分析將程式庫建置並部署到 NuGet

Alessandro Del Del |2015 年 10 月 |取得程式碼: C#VB

(也稱為"Roslyn"程式碼基底) 的 Microsoft.NET 編譯器平台提供公開 (expose),還有其他豐富的程式碼分析應用程式開發介面能運用於建立整合到 Visual Studio 2015 程式碼編輯器的即時分析規則的開放原始碼 C# 和 Visual Basic 編譯器。.NET 編譯器平台,您可以撰寫自訂的定義域專屬的程式碼分析器和重構讓 Visual Studio 可以偵測到程式碼問題,當您輸入時,報告警告和錯誤訊息。.NET 編譯器平台的最大的優點是您可以使用您 Api 包裝程式碼分析器。比方說,如果您建立文件庫或可重複使用的使用者控制項,您可以與您的程式庫一起出貨分析器並為開發人員提供較佳的程式碼撰寫體驗。

在本文中我將說明如何配套成線上部署的 NuGet 封裝的程式庫和分析器顯示您可以如何提供整合式的 Roslyn 程式碼分析您的應用程式開發介面。這需要您有關於.NET 編譯器平台概念以及撰寫程式碼分析工具至少有基本知識。Alex Turner 已經將這些主題將討論在過去的 MSDN 雜誌文章: "C# 和 Visual Basic: 使用 Roslyn 即時程式碼分析工具撰寫您的應用程式開發介面 」 (msdn.microsoft.com/magazine/dn879356) 和"C# — Roslyn analyzer 加入程式碼修正"(msdn.microsoft.com/magazine/dn904670),你強烈建議您在這裡的之前讀取的。

準備以模擬自訂 Api 的範例程式庫

首先您必須是模擬的自訂 API 的類別程式庫。本文的範例程式庫公開一個簡單的公用方法擷取來自 RSS 餵送,傳回的摘要項目集合的一般資訊。在 Visual Studio 2015 中建立新可攜式類別庫稱為 FeedLibrary 與 C# 或 Visual Basic 並確保最小的目標是 Windows 8.1、 Windows Phone 8.1 及 Microsoft.NET Framework 4.5.1。使用此目標,程式庫也可以利用 Async/Await 模式與不需要額外。

重新命名 Class1.vb 或 Class1.cs FeedItem.vb/.cs 產生檔案。此類別的 C# 程式碼位於 [圖 1 和 Visual Basic 程式碼位於 [圖 2

[圖 1 在 C# 中實作類別來擷取一般項目從 RSS 摘要

using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace FeedLibrary
{
  // Represent a single content in the RSS feed
  public class FeedItem
  {
    // Properties representing information,
    // which is common to any RSS feed
    public string Title { get; set; }
    public string Author { get; set; }
    public string Description { get; set; }
    public DateTimeOffset PubDate { get; set; }
    public Uri Link { get; set; }
    // Return a collection of FeedItem objects from a RSS feed
    public static async Task<IEnumerable<FeedItem>> ParseFeedAsync(
      string feedUrl)
    {
      var client = new HttpClient();
      // Download the feed content as a string
      var result = await client.GetStringAsync(new Uri(feedUrl));
      // If no result, throw an exception
      if (result == null)
      {
        throw new InvalidOperationException(
          "The specified URL returned a null result");
      }
      else
      {
        // LINQ to XML: Convert the returned string into an XDocument object
        var doc = XDocument.Parse(result);
        var dc = XNamespace.Get("http://purl.org/dc/elements/1.1/");
        // Execute a LINQ query over the XML document
        // and return a collection of FeedItem objects
        var query = (from entry in doc.Descendants("item")
                     select new FeedItem
                     {
                         Title = entry.Element("title").Value,
                         Link = new Uri(entry.Element("link").Value),
                         Author = entry.Element(dc + "creator").Value,
                         Description = entry.Element("description").Value,
                         PubDate = DateTimeOffset.Parse(
                           entry.Element("pubDate").Value,
                     System.Globalization.CultureInfo.InvariantCulture)
                     });
        return query;
      }
    }
  }
}

[圖 2 實作類別來擷取一般項目從 Visual Basic 中的 RSS 摘要

Imports System.Net.Http
Imports <xmlns:dc="http://purl.org/dc/elements/1.1/">
'Represent a single content in the RSS feed
Public Class FeedItem
  'Properties representing information
  'which is common to any RSS feed
  Public Property Title As String = String.Empty
  Public Property Author As String = String.Empty
  Public Property Description As String = String.Empty
  Public Property PubDate As DateTimeOffset
  Public Property Link As Uri
  'Return a collection of FeedItem objects from a RSS feed
  Public Shared Async Function ParseFeedAsync(feedUrl As String) As _
    Task(Of IEnumerable(Of FeedItem))
    Dim client As New HttpClient
    'Download the feed content as a string
    Dim result = Await client.GetStringAsync(New Uri(feedUrl, UriKind.Absolute))
    'If no result, throw an exception
    If result Is Nothing Then
       Throw New InvalidOperationException(
         "The specified URL returned a null result")
    Else
      'LINQ to XML: Convert the returned string
      'into an XDocument object
      Dim document = XDocument.Parse(result)
      'Execute a LINQ query over the XML document
      'and return a collection of FeedItem objects
      Dim query = From item In document...<item>
                  Select New FeedItem With {
                  .Title = item.<title>.Value,
                  .Author = item.<dc:creator>.Value,
                  .Description = item.<description>.Value,
                  .PubDate = DateTimeOffset.Parse(item.<pubDate>.Value),
                  .Link = New Uri(item.<link>.Value)}
      Return query
    End If
  End Function
End Class

程式碼非常簡單: 下載新聞訂閱方式從指定的內容 RSS 摘要 URL 建立每個摘要的項目 FeedItem 類別的執行個體和它最後會傳回新的項目集合。若要使用的程式庫,您只是叫用靜態 ParseFeedAsyncAsync 方法的 C#,如下所示:

// Replace the argument with a valid URL
var items = await FeedItem.ParseFeedAsyncAsync("http://sampleurl.com/rss");

和 Visual basic 中看起來像這樣:

'Replace the argument with a valid URL
Dim items = Await FeedItem.ParseFeedAsyncAsync("http://sampleurl.com/rss")

這個引動過程會傳回 IEnumerable < FeedItem > 您可以用它根據自己的需求。選取 [發行] 組態並建置專案。此時,Visual Studio 2015 產生呼叫 FeedLibrary.dll,稍後將會使用程式庫。

撰寫 Roslyn 分析器

下一步建立自訂 Api 會提供網域特定即時分析規則 Roslyn 分析器。分析程式會偵測是否做為 ParseFeedAsyncAsync 方法的引數提供的 URL 為語式正確使用 Uri.IsWellFormedUriString 方法。如果沒有,當您輸入 [分析器] 將會報告警告。當然有許多種方式來偵測如果 URL 不正確,但我使用為了簡單起見這一個。此外,基於相同原因,分析器會提供即時的分析報告警告,但它將無法提供任何程式碼修正程式,這留給您做練習。話雖如此,請遵循下列步驟:

  1. 在 [方案總管] 中以滑鼠右鍵按一下方案名稱,然後選取 [新增] |新的專案。
  2. 在擴充性節點的專案範本清單中,選取 [分析程式與程式碼修正 (NuGet + VSIX)。請注意預設不會出現 [擴充] 節點。您需要先下載.NET 編譯器平台 SDK 來擷取與程式碼修正範本專案分析器。在 [新增專案] 對話方塊和您的搜尋分析器會看到範本專案來下載此 SDK。
  3. 呼叫新的分析器 FeedLibraryAnalyzer 並按一下 [確定]。
  4. 準備新專案時,移除 CodeFixProvider.cs (或.vb) 檔案。

在 DiagnosticAnalyzer.cs (或.vb) 檔案中進行第一件事是供應這些字串會識別在程式碼編寫經驗分析器。為了讓 「 分析 」 的實作更簡單,我可以在目前的範例中使用一般字串來取代 LocalizableString 物件和資源檔,假設分析程式不需要有當地語系化版本。重寫 DiagnosticId、 標題、 訊息、 描述和類別的 C#,如下所示:

public const string DiagnosticId = "RSS001";
internal static readonly string Title = "RSS URL analysis";
internal static readonly string MessageFormat = "URL is invalid";
internal static readonly string Description =
  "Provides live analysis for the FeedLibrary APIs";
internal const string Category = "Syntax";

和 Visual basic,如下所示:

Public Const DiagnosticId = "RSS001"
Friend Shared ReadOnly Title As String = "RSS URL analysis"
Friend Shared ReadOnly MessageFormat As String = "URL is invalid"
Friend Shared ReadOnly Description As String =
  "Provides live analysis for the FeedLibrary APIs"
Friend Const Category = "Syntax"

請勿變更診斷的嚴重性,這是預設的警告目前的範例是適當的選擇。現在正是把重心放在分析邏輯的時候。分析程式必須檢查程式碼會叫用呼叫 ParseFeedAsync 方法。若是如此,分析程式然後會檢查所提供的 URL 是否正確。使用語法視覺化檢視的協助,您可以看到在 [圖 3 InvocationExpression 對應物件的型別 InvocationExpressionSyntax 呈現 ParseFeedAsync 方法引動過程的方式。

語法視覺化檢視可以協助尋找適當語法節點表示法
[圖 3 語法視覺化檢視可以協助尋找適當語法節點表示法

因此分析器僅著重於 InvocationExpressionSyntax; 類型的物件當找到,它會將相關聯的運算式轉換成類型 MemberAccessExpressionSyntax,包含方法呼叫的相關資訊的物件中。如果轉換成功,analyzer 會檢查如果 ParseFeedAsync 方法,然後擷取第一個引數並在它的值上執行即時的分析。這是新的方法呼叫 AnalyzeMethod 適用於 SyntaxNode 層級,以表示 [圖 4 C# 和 [圖 5 Visual basic。

[圖 4 偵測 C# 中的 ParseFeedAsync 引數的問題

private static void AnalyzeMethodInvocation(SyntaxNodeAnalysisContext context)
{
  // Convert the current syntax node into an InvocationExpressionSyntax,
  // which represents a method call
  var invocationExpr = (InvocationExpressionSyntax)context.Node;
  // Convert the associated expression into a MemberAccessExpressionSyntax,
  // which represents a method's information
  // If the expression is not a MemberAccessExpressionSyntax, return
  if (!(invocationExpr.Expression is MemberAccessExpressionSyntax))
  {
    return;
  }
  var memberAccessExpr = (MemberAccessExpressionSyntax)invocationExpr.Expression;
  // If the method name is not ParseFeedAsync, return
  if (memberAccessExpr?.Name.ToString() != "ParseFeedAsync") { return; }
  // If the method name is ParseFeedAsync, check for the symbol
  // info and see if the return type matches
  var memberSymbol = context.SemanticModel.
                     GetSymbolInfo(memberAccessExpr).
                     Symbol as IMethodSymbol;
  if (memberSymbol == null) { return; }
  var result = memberSymbol.ToString();
  if (memberSymbol?.ReturnType.ToString() !=
  "System.Threading.Tasks.Task<
    System.Collections.Generic.IEnumerable<FeedLibrary.FeedItem>>")
  {
      return;
  }
  // Check if the method call has the required argument number
  var argumentList = invocationExpr.ArgumentList;
  if (argumentList?.Arguments.Count != 1) {
      return; }
  // Convert the expression for the first method argument into
  // a LiteralExpressionSyntax. If null, return
  var urlLiteral = (LiteralExpressionSyntax)invocationExpr.ArgumentList.
      Arguments[0].Expression;
  if (urlLiteral == null) { return; }
  // Convert the actual value for the method argument into string
  // If null, return
  var urlLiteralOpt = context.SemanticModel.GetConstantValue(urlLiteral);
  var urlValue = (string)urlLiteralOpt.Value;
  if (urlValue == null) { return; }
  // If the URL is not well-formed, create a diagnostic
  if (Uri.IsWellFormedUriString(urlValue, UriKind.Absolute) == false)
  {
    var diagn = Diagnostic.Create(Rule, urlLiteral.GetLocation(),
      "The specified parameter Is Not a valid RSS feed");
    context.ReportDiagnostic(diagn);
  }
}

[圖 5 在 Visual Basic 中偵測 ParseFeedAsync 引數的問題

Private Sub Shared AnalyzeMethodInvocation(context As SyntaxNodeAnalysisContext)
  'Convert the current syntax node into an InvocationExpressionSyntax
  'which represents a method call
  Dim invocationExpr = CType(context.Node, InvocationExpressionSyntax)
  'Convert the associated expression into a MemberAccessExpressionSyntax
  'which represents a method's information
  'If the expression Is Not a MemberAccessExpressionSyntax, return
  If TypeOf invocationExpr.Expression IsNot MemberAccessExpressionSyntax Then Return
  Dim memberAccessExpr = DirectCast(invocationExpr.Expression,
    MemberAccessExpressionSyntax)
  'If the method name Is Not ParseFeedAsync, return
  If memberAccessExpr?.Name.ToString <> "ParseFeedAsync" Then Return
  'If the method name is ParseFeedAsync, check for the symbol info
  'and see if the return type matches
  Dim memberSymbol = TryCast(context.SemanticModel.
      GetSymbolInfo(memberAccessExpr).Symbol, IMethodSymbol)
  If memberSymbol Is Nothing Then Return
  Dim result = memberSymbol.ToString
  If Not memberSymbol?.ReturnType.ToString =
    "System.Threading.Tasks.Task(Of System.Collections.Generic.IEnumerable(
    Of FeedLibrary.FeedItem))"
    Then Return
  'Check if the method call has the required argument number
  Dim argumentList = invocationExpr.ArgumentList
  If argumentList?.Arguments.Count <> 1 Then Return
  'Convert the expression for the first method argument into
  'a LiteralExpressionSyntax. If null, return
  Dim urlLiteral =
    DirectCast(invocationExpr.ArgumentList.Arguments(0).GetExpression,
      LiteralExpressionSyntax)
  If urlLiteral Is Nothing Then Return
  'Convert the actual value for the method argument into string
  'If null, return
  Dim urlLiteralOpt = context.SemanticModel.GetConstantValue(urlLiteral)
  Dim urlValue = DirectCast(urlLiteralOpt.Value, String)
  If urlValue Is Nothing Then Return
  'If the URL Is Not well-formed, create a diagnostic
  If Uri.IsWellFormedUriString(urlValue, UriKind.Absolute) = False Then
     Dim diagn = Diagnostic.Create(Rule, urlLiteral.GetLocation,
       "The specified parameter Is Not a valid RSS feed")
     context.ReportDiagnostic(diagn)
  End If
End Sub

此時,您需要編輯呼叫新加入的 AnalyzeMethodInvocation 方法,Initialize 方法中所示 [圖 6 C# 和 [圖 7 Visual basic。

[圖 6 編輯 C# 中的初始化方法

public override void Initialize(AnalysisContext context)
{
  // Register an action when compilation starts
  context.
    RegisterCompilationStartAction((CompilationStartAnalysisContext ctx) =>
  {
    // Detect if the type metadata
    // exists in the compilation context
    var myLibraryType =
    ctx.Compilation.
    GetTypeByMetadataName("FeedLibrary.FeedItem");
    // If not, return
    if (myLibraryType == null)
        return;
    // Register an action against an InvocationExpression
    ctx.RegisterSyntaxNodeAction(AnalyzeMethodInvocation,
      SyntaxKind.InvocationExpression);
  });
}

[圖 7 編輯在 Visual Basic 中的 Initialize 方法

Public Overrides Sub Initialize(context As AnalysisContext)
  ' Register an action when compilation starts
  context.
    RegisterCompilationStartAction
    (Sub(ctx As CompilationStartAnalysisContext)
      'Detect if the type metadata
      'exists in the compilation context
      Dim myLibraryType =
        ctx.Compilation.
          GetTypeByMetadataName("FeedLibrary.FeedItem")
        'If not, return
        '(no reference to the library)
        If myLibraryType Is Nothing
Then Return
        'Register an action against
        'an InvocationExpression
        ctx.RegisterSyntaxNodeAction(
          AddressOf AnalyzeMethodInvocation,
          SyntaxKind.InvocationExpression)
     End Sub)
End Sub

請注意如何將程式碼首先會檢查是否程式庫的參考存在專案中叫用 Compilation.GetTypeMetadataName 方法,其引數是必須存在於目前的內容並確定已加入的參考型別的名稱。如果這個引動過程會傳回 null,則表示型別不存在,因此,任何參考已加入至文件庫。因此,不是需要註冊程式碼分析動作、 改善分析器] 的效能。如果您現在按 F5 以在 Visual Studio 2015 的實驗執行個體中測試分析器] 並建立新的專案與 FeedLibrary 程式庫的參考,您可以看到它正確報告的方式警告每次您提供無效的 URL 中所示 [圖 8

如果 URL 不是語式正確分析器會報告警告
[圖 8 分析器報告警告 URL 格式不正確

到目前為止您已經建置應用程式開發介面和相關的定義域專屬的程式碼分析規則。現在就可以開始了解如何結合到單一的 NuGet 封裝。

建立 NuGet 封裝包含應用程式開發介面和分析器

以程式碼修正專案範本分析器的 MSBuild 規則自動產生包含已編譯的分析器中,發佈至 NuGet 儲存機制可以與其他開發人員共用的 NuGet 封裝。實際上,每次按下 F5 或建置分析器專案偵錯分析器 Visual Studio 2015 會重建分析器.dll 檔案 (在目前的範例 FeedLibraryAnalyzer.dll) 和可轉散發套件的 NuGet 封裝包含 「 分析 」。

建置程序也會產生您可以發行到 Visual Studio 組件庫和也用來偵錯的 Visual Studio 2015 實驗性的執行個體內分析器但這超出本文的範圍並不這裡所涵蓋的 vsix。

如果您想要整合式 Roslyn 分析與共用文件庫,您需要將程式庫加入至 Visual Studio 2015 在建置專案時所產生的 NuGet 封裝。進行這個動作之前您需要稍微了解如何進行分析的 NuGet 套件。實際上,NuGet 封裝是.nupkg 副檔名的.zip 封存檔。因此,您可以輕鬆地調查的內容和結構具有.zip archiver 工具,例如 Windows 檔案總管壓縮資料夾工具,WinZip 或 WinRar 的 NuGet 封裝。以下是最重要的項目設計用於部署分析器 NuGet 封裝中的摘要:

  • .nuspec 檔案: 這個檔案包含封裝的中繼資料和包含所需的發行集,例如封裝名稱、 版本、 描述、 作者、 授權 URL 和其他資訊。.Nuspec 檔案被包含您在 [方案總管] 中看到分析器專案內 Diagnostic.nuspec 檔案為基礎的 NuGet 封裝。您要稍後編輯在 Visual Studio 2015 Diagnostic.nuspec。
  • 工具] 資料夾中: 此資料夾包含 Visual Studio 用來安裝 (Install.ps1) 和解除安裝 (Uninstall.ps1) 指定的專案分析器的 Windows PowerShell 指令碼。
  • 分析器資料夾: 此資料夾包含分析器.dll 檔案組織成特定的子資料夾。無從驗證的分析程式庫 (也就,針對所有語言) 位於名為 dotnet 子資料夾中。針對 C# 的分析器位於 dotnet\cs 的子資料夾中而以 Visual Basic 為目標的解析程式都位於稱為 dotnet\vb 的資料夾。值得一提的是該 dotnet 表示.NET 核心 NuGet 設定檔和支援 Windows 通用應用程式和 ASP.NET 5 專案等專案類型。

有許多其他項目可以包含 NuGet 封裝,但此處我把重點放在典型的封裝產生 Roslyn 分析程式只能在將必要的項目會討論。

任何可以從 Visual Studio 專案參考的程式庫必須組織成一個名為 lib 資料夾中。因為程式庫可以鎖定不同的平台,例如不同版本的.NET Framework、 Windows 執行階段不同版本的 Windows Phone 或可攜式子集 (包括 Xamarin 程式庫) lib 資料夾必須包含一個子資料夾中每個目標的平台和每一個子資料夾必須包含一份部署文件庫。每一個子資料夾名稱必須符合所謂的設定檔代表特定的平台的名稱。比方說,如果您有.NET Framework 4.5.1 和 Windows 8.1 的程式庫,您就必須下列結構其中 net451 是.NET Framework 4.5.1 的設定檔名稱而 netcore451 是在 Windows 8.1 的 Windows 執行階段的設定檔名稱:

lib\net451\mylib.dll
lib\netcore451\mylib.dll

值得一提的是 uap10.0 設定檔為目標來建置 Windows 10 應用程式通用 Windows 平台 (UWP)。使用 NuGet 文件中支援的設定檔的完整清單。先前建立的範例程式庫是.NET Framework 4.5.1、 Windows 8.1 和 Windows Phone 8.1 的可攜式程式庫。這種目標的設定檔名稱是可攜式 net451 + netcore451 + wpa81 和必須用來命名的子資料夾將包含 NuGet 封裝中的程式庫。您不需要建立子資料夾及手動; 複製文件庫您只需要編輯 Visual Studio 內部的 NuGet 套件中繼資料 (Diagnostic.nuspec 檔案)。[圖 9 顯示更新中繼資料發行 (識別碼、 標題、 作者、 描述、 授權等) 和新的檔案項目中指定的原始程式檔和目標子資料夾的檔案節點的適當資訊。

[圖 9 編輯 NuGet 套件中繼資料

<?xml version="1.0"?>
<package xmlns="https://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
  <metadata>
    <id>RSSFeedLibrary</id>
    <version>1.0.0.0</version>
    <title>RSS Feed Library with Roslyn analysis</title>
    <authors>Alessandro Del Sole</authors>
    <owners>Alessandro Del Sole</owners>
    <licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
    <!-- Removing these lines as they are not needed
    <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
    <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>-->
    <requireLicenseAcceptance>true</requireLicenseAcceptance>
    <description>Companion sample for the "Build and Deploy Libraries
    with Integrated Roslyn Code Analysis to NuGet" article
    on MSDN Magazine</description>
    <releaseNotes>First release.</releaseNotes>
    <copyright>Copyright 2015, Alessandro Del Sole</copyright>
    <tags>RSS, analyzers, Roslyn</tags>
    <frameworkAssemblies>
      <frameworkAssembly assemblyName="System" targetFramework="" />
    </frameworkAssemblies>
  </metadata>
  <files>
    <file src="*.dll" target="analyzers\dotnet\cs"
          exclude="**\Microsoft.CodeAnalysis.*;
          **\System.Collections.Immutable.*;**\System.Reflection.Metadata.*;
          **\System.Composition.*" />
    <file src="tools\*.ps1" target="tools\" />
    <file src="lib\FeedLibrary.dll" target="lib\portable-net451+netcore451+wpa81\"/>
  </files>
</package>

Src 屬性表示該媒體櫃的來源位置和目標指定適當的設定檔名稱為基礎的目標子資料夾。在此情況下,Visual Studio 會搜尋名為 lib,您必須在目前的目錄中建立的資料夾內呼叫 FeedLibrary.dll 文件庫。後者是包含編譯的 analyzer 的資料夾通常版本或偵錯資料夾,視所選取的組建組態而定。根據目前的範例,您必須建立一個稱為 [版本] 資料夾內的 lib 資料夾則複製 (產生編譯範例程式庫在開始時) FeedLibrary.dll 到 lib 資料夾。一旦您已經完成此作業,您可以只重建您的方案和 Visual Studio 會產生包含文件庫與 Roslyn 分析器更新的 NuGet 封裝。

值得注意的是當您重新建置專案,Visual Studio 2015 會自動更新 NuGet 套件的組建和修訂版本號碼忽視版本標記中提供的數字。NuGet 封裝之更新的內容可以輕鬆地調查.zip archiver 工具開啟 NuGet 封裝的更新最高版本。請記住: 每當您變更 id 項目值做為在 [圖 9, ,Visual Studio 2015 會產生具有不同的名稱,並根據新的識別碼的 NuGet 封裝。在此情況下,兩者都在自動提供變更 RSSFeedLibrary 結果稱為的 RSSFeedLibrary.1.0.xxx.yyy.NuPkg 其中 xxx 是組建版本號碼而 yyy 是修訂版本編號,NuGet 封裝中的識別碼值建置時間。此時您已達到第一個目標: 封裝使用的自訂 Api 整合成一個 NuGet 套件的 Roslyn 分析。

或者,您無法建立 (和發行) 兩個不同的封裝 — 一個用於分析程式還有一個程式庫 — 和第三個空白做為相依性解析它們一起提取的 NuGet 封裝。使用此方法時,您可以決定保留安裝的大小較小使用 Api 只是不分析器] 中,但您必須先熟悉 NuGet 慣例來手動從頭開始建立封裝。發行之前所產生之新封裝到線上的 NuGet 存放庫,最好在本機加以測試。

測試本機 NuGet 封裝

Visual Studio 2015 允許從本機儲存機制挑選 NuGet 封裝。這是非常有用,尤其是在您需要快取封裝常使用或您可能需要離線工作時的情況下。請注意這是很好的解決方案時的本機資料夾中的封裝數目很小。如果您有數百個套件,就會以處理更複雜。為了測試先前產生的 NuGet 套件,稱為 LocalPackages,磁碟上建立新的資料夾則 RSSFeedLibrary.1.0.xxx.yyy.nupkg 檔案的最新版本複製到這個資料夾。下一個步驟是啟用 Visual Studio 2015 挑選封裝從指定的本機資料夾中所示 [圖 10; 選取 [工具] |選項 |NuGet 封裝管理員 |封裝來源,並在 [可用的封裝來源] 方塊中按一下 [新增] 按鈕 (以綠色加法符號)。此時,Name 和 Source 文字方塊中輸入本機封裝和 C:\LocalPackages,分別。最後,按一下 [重新整理封裝來源清單的更新。

做為 NuGet 封裝來源加入本機儲存機制
[圖 10 做為 NuGet 封裝來源加入本機儲存機制

您已經有本機的 NuGet 存放庫,在 Visual Studio 2015 建立新的主控台應用程式測試的程式庫以及 Roslyn 分析封裝。儲存專案,然後選取 [專案] |管理 NuGet 封裝。NuGet 封裝管理員] 視窗出現時,封裝來源下拉式方塊中選取本機封裝來源。此時,封裝管理員會顯示可用的封裝清單內指定的儲存機制,在此情況下範例封裝。

按一下 [安裝]。如同線上 NuGet 封裝,Visual Studio 會顯示所選封裝的摘要資訊並將會要求您接受授權合約。完成安裝時,您將能夠看到文件庫和 [方案總管] 中的 Roslyn 分析器] 中所示 [圖 11

文件庫與 Roslyn 分析器安裝透過 NuGet 封裝
[圖 11 此時文件庫] 和 [Roslyn 分析器安裝透過 NuGet 封裝

如果您只要撰寫一些程式碼會使用 FeedItem.ParseFeedAsync 方法來做為引數傳遞無效的 URL、 即時的分析引擎就會報告一則警告訊息,如預期般 (請參閱 [圖 8 參考)。

當您安裝分析器時,不論如果它由您或其他開發人員所產生您可以查看在 [方案總管] 中的每個規則的詳細資料依序展開參考、 分析器、 和 [分析器] 的名稱。在此情況下,展開 FeedLibraryAnalyzer 名稱即可查看 RSS URL 分析規則中所示 [圖 11。當您按一下規則時,[屬性] 視窗中顯示的詳細資訊的預設和有效的嚴重性,例如如果啟用,以及完整的規則描述。此外,您可以使用規則集編輯器來檢視所有的規則適用於專案和檢視或變更規則的重要性以及停用或啟用分析器和規則。開啟規則集編輯器、 在 [方案總管] 中按兩下 [內容] 然後專案的 [屬性] 視窗中選取 [程式碼分析] 索引標籤,最後按一下開啟、 預設規則集保持不變。

您可以看到從 [圖 11, 、 停用/啟用規則即可取消選取/選取靠近規則程式碼的核取方塊,您就可以按一下黑色向下目前的嚴重性層級 (也可以以滑鼠右鍵按一下 [方案總管] 中的規則然後從內容功能表中選取 [設定規則設定嚴重性) 右側的箭號來變更預設嚴重性。當您滿意本機測試時,您可以移至發行 NuGet 封裝。

測試封裝線上

在 NuGet 上開發人員是期望以尋找高品質、 更專業的套件。基於這個理由,您將封裝發佈到線上的 NuGet 組件庫、 之前您應該測試您的協助可讓您建立私用的 NuGet 存放庫和摘要的線上服務的工作並穩定套件一旦畢業到官方的 NuGet 存放庫。由 MyGet 提供很好的選擇 (myget.org)、 線上服務,可讓建立個人和企業 NuGet 摘要、 再加上 VSIX、 npm 及 Bower 摘要。MyGet 提供免費的計劃與發佈和使用 NuGet 封裝所需的最常見功能免費的計劃不允許您建立私用摘要 (您將需要的付費的方案),但它是絕佳的選擇來測試您的封裝使用如預期般從線上儲存機制。當您註冊時,您必須建立摘要 NuGet 的選項。比方說,我公開摘要 MyGet 上的將會位於 myget.org/F/aledelsole/api/v2。說明如何使用 MyGet 超出本文的範圍但文件完整說明如何設定摘要您 NuGet。一旦您建立摘要並發佈您的封裝,您只要啟用 Visual Studio 2015 可挑選摘要 MyGet NuGet 封裝。若要這麼做,您可以依照上一節中所述的步驟並採取 [圖 10 做為參考,提供您 MyGet 摘要的 URL。若要下載和測試封裝在 Visual Studio 中,您將仍遵循上一節所述的步驟很明顯地選取為 [NuGet Package Manager] 視窗中的來源摘要 MyGet。

線上 NuGet 組件庫來發佈封裝

若要將封裝發佈到線上的 NuGet 存放庫,您必須開啟 nuget.org 和登入的帳戶。如果您沒有帳戶,按一下頁面右上角 [暫存器/登入] 超連結。您可以登入 Microsoft 帳戶 (建議) 或使用者名稱/密碼認證。註冊完成後,按一下 [上傳封裝。精靈會要求您的第一件事是指定您想要上傳因此按一下 [瀏覽、 選取 RSSFeedLibrary.1.0.xxx.yyy.nupkg 的最新版本從磁碟,然後按一下 [上傳的 NuGet 封裝。

現在您將會提示您封裝的中繼資料資訊。您可以選擇先將它發行至圖庫檢視封裝詳細資料中所示的這裡 [圖 12。準備就緒時,按一下 [提交]。此時,其中包含您的應用程式開發介面和整合式的 Roslyn 分析器封裝將會發行至 NuGet 組件庫。請注意它通常會採用 15 至 20 分鐘才能看到在 Visual Studio 2015 [NuGet Package Manager] 視窗中可用的封裝。

檢閱封裝之前發行集的詳細資料
[圖 12 檢閱套件之前發行集的詳細資料

封裝會列在組件庫之後,您可以安裝到您的專案從線上 NuGet 存放庫中所示 [圖 13。安裝之後,您將使用文件庫中所述上一節中,由.NET 編譯器平台的整合式即時、 定義域專屬的程式碼分析。

NuGet 封裝也可以公開從線上儲存機制
[圖 13] NuGet 套件也可以公開從線上儲存機制

封裝的更新

與任何 NuGet 封裝,您可以改善您的程式庫並將更新的封裝版本發送至 NuGet。若要建立更新的封裝,只需重建您的方案和 Visual Studio 2015 就會自動更新套件版本號碼。然後重新建置專案並重複先前所述來發佈 NuGet 套件的步驟。NuGet 將處理的每個封裝的可用版本清單並可以讓開發人員選擇所需的版本。

總結

其中一個.NET 編譯器平台所提供的最大優點是您可以建立網域特有的程式碼分析規則 api。在本文中我先示範如何建立範例程式庫則如何建立在打字時,偵測到程式碼問題 Roslyn 分析器特定媒體櫃中的成員。接下來,我示範了如何配套成一個是發行項的核心的 NuGet 套件的文件庫與分析器。如此一來,下載 NuGet 封裝的開發人員會使用整合式 Roslyn 即時分析 Api。我再移討論如何在線上發佈之前測試 NuGet 封裝在本機。因為您有機會確認文件庫/分析器組能夠適當運作之前您將它提供給大眾這個步驟是非常重要的。但好戲是其他開發人員可以使用您的工作的結果中的發行項的最後一節我示範了如何將封裝發佈到線上的 NuGet 存放庫,因此如何能更新安裝到您的專案從 Visual Studio。部署您的程式庫一起 Roslyn 分析器肯定會增加工作給其他開發人員提供更好的程式碼撰寫體驗的值。


Alessandro Del Sole自 2008年已是 Microsoft MVP。獎勵年五倍的 MVP,他著有許多線上叢書 》、 電子書、 解說影片及有關使用 Visual Studio.NET 開發文件。您可以在 Twitter 上追隨他 @progalex

衷心感謝以下技術專家對本文的審閱: Srivatsn Narayanan 和 Manish Vasani
Srivatsn Narayanan 曾語言 (IronPython、 C#、 VB.NET) 在 Microsoft 中最後一個八年的時間。他曾經編譯器和這些語言的語言服務工作並具有已最近前置努力開發 Roslyn analyzer\fixer 架構。

Manish Vasani 是與 Visual Studio 群組中的不同團隊在 Microsoft 任職八年經驗的軟體工程師。他是目前的 Managed 語言 Analysis Services 小組,其中他擁有 Roslyn 分析器 Api,analyzer 執行基礎結構和它與 Visual Studio 整合。