コントリビューションと構成

特定の基底クラスから派生することで、拡張機能コンポーネントを Visual Studio に公開できます。また、特定のプロパティを定義し、さまざまな属性を使用して構成することもできます。

Visual Studio の投稿

Visual Studio 拡張機能の目的は、Visual Studio に 新機能を提供 するためです。 これを実現するには、多くのクラス Commandの 1 つを拡張するか、 ToolWindow属性 ExtensionPartVisualStudioContribution 適用します。

この記事では、 コマンドの親のサンプル拡張機能を 参照して、拡張機能コンポーネントの提供と構成の概念について説明します。

すべての VisualStudio.Extensibility 拡張機能は、少なくとも 1 つの Extension クラスを提供する必要があります。

namespace CommandParentingSample;

[VisualStudioContribution]
public class CommandParentingSampleExtension : Extension
{
    /// <inheritdoc/>
    protected override void InitializeServices(IServiceCollection serviceCollection)
    {
        base.InitializeServices(serviceCollection);
    }
}

クラスは Extension 拡張機能の最初のインスタンス化されたクラスであり、依存関係の挿入に使用する独自の IServiceCollection サービスを追加できます。

コマンドの親のサンプルは、Visual Studio に別のクラス a Commandを提供します。

[VisualStudioContribution]
internal class SampleCommand : Command
{
    public SampleCommand()
    {
    }
    ...

VisualStudio.Extensibility SDK によって提供される基本クラスを拡張する場合は、基本クラスが実装IVisualStudioContributionClassされているかどうかをチェックすることで、属性を使用VisualStudioContributionすることが予想されるかどうかを確認できます (両方ExtensionCommandとも実装します)。

Visual Studio コントリビューション クラスは遅延してインスタンス化されたシングルトンです。インスタンスは 1 つだけ作成され、Visual Studio が操作する必要があるまで (たとえば、ユーザーが最初に呼び出す場合 Command など)、インスタンスの作成が遅れます。

VisualStudio.Extensibility インフラストラクチャを使用すると、依存関係の挿入を通じて、Visual Studio コントリビューション クラスのコンストラクター パラメーターとしてサービスを受け取ることもできます (挿入用の SDK によって提供されるサービスを参照)。これには、クラスのInitializeServicesメソッドにIServiceCollectionExtension追加したサービスも含まれます。

Visual Studio では、多くの場合、一意の識別子をコントリビューションに関連付ける必要があります。 ほとんどの場合、VisualStudio.Extensibility インフラストラクチャでは、Visual Studio コントリビューション クラスの完全な名前がコントリビューション識別子として使用されます。 たとえば、上記のクラスの Extension 識別子は CommandParentingSample.CommandParentingSampleExtension. Visual Studio コントリビューション クラスの型名と名前空間は、Visual Studio のログとエラー メッセージに表示される可能性があるため、慎重に選択できます。

Visual Studio のコントリビューションの構成

ほとんどの Visual Studio コントリビューション クラスでは、構成が必要または許可されます。 たとえば、抽象クラスでは Command 、少なくともコマンドの CommandConfiguration 表示名と、必要に応じて配置などの他のプロパティを指定するプロパティの実装が必要です。

[VisualStudioContribution]
internal class SampleCommand : Command
{
    /// <inheritdoc />
    public override CommandConfiguration CommandConfiguration => new("%CommandParentingSample.SampleCommand.DisplayName%")
    {
        Placements = new[]
        {
            // File in project context menu
            CommandPlacement.VsctParent(new Guid("{d309f791-903f-11d0-9efc-00a0c911004f}"), id: 1072, priority: 0),

            // Project context menu
            CommandPlacement.VsctParent(new Guid("{d309f791-903f-11d0-9efc-00a0c911004f}"), id:  1026, priority: 0),

            // Solution context menu
            CommandPlacement.VsctParent(new Guid("{d309f791-903f-11d0-9efc-00a0c911004f}"), id:  1043, priority: 0),
        },
    };
    ...

CommandConfigurationはコンパイル時定数です。これは、拡張機能がビルドされ、拡張機能マニフェスト (extension.json) に含まれるときに、その値が評価されることを意味します。 Visual Studio では、拡張機能自体を読み込まずに拡張機能マニフェストを読み取ることができるため、パフォーマンスが向上します。

コンパイル時定数 には、通常のプロパティと比較して追加の制限が適用されます。たとえば、読み取り専用にする必要があり、初期化コードに非静的メンバーまたは複数ステートメント命令型コード ブロックへの参照を含めることはできません。 これらの制限は VisualStudio.Extensibility ビルド ツールによって適用され、次のようなエラー メッセージが表示されます。

コンパイル時定数 SampleCommand.CommandConfiguration を評価するときに問題が発生しました。 コンパイル時定数値を評価する場合、ユーザー定義の非静的メンバーへの参照はサポートされません。

一般に、拡張機能は実行時にコンパイル時定数構成プロパティを参照しないでください。

コンパイル時定数構成プロパティの定義には属性があるため、簡単にCompileTimeEvaluation識別できます。

public abstract class Command : ExecutableCommandHandler, IVisualStudioContributionClass
{
    ...
    /// <summary>
    /// Gets the configuration for this command. The value of this property is evaluated at compile time
    /// when building the Visual Studio extension.
    /// </summary>
    [CompileTimeEvaluation]
    public abstract CommandConfiguration CommandConfiguration { get; }
    ...

まれに、構成プロパティは省略可能な場合があります。 場合によっては、同じクラスに複数の構成プロパティを実装することが必要になる場合があります。 これは、複数のインターフェイスを拡張して実装する ExtensionPart 場合に一般的であり、それぞれが独自の構成プロパティを必要とします。

スタンドアロン構成プロパティ

前述のように、Visual Studio コントリビューション クラスは、通常、1 つ以上 のコンパイル時定数構成プロパティを公開するシングルトン クラスを 定義します。 構成プロパティの値は、拡張メタデータとして保存されます。

一部の拡張機能では、どのクラスにも関連付けられていない拡張メタデータを指定する必要があり、それ自体が意味を持つか、他の構成によって参照されることを意図しています。 いくつかの例として、メニュー、ツール バー、ドキュメントの種類の定義があります。 これは、静的な読み取り VisualStudioContribution 専用構成プロパティに属性を適用することによって実現されます。

Visual Studio のコントリビューション プロパティは、任意のクラスに配置できます。

コマンドの親のサンプルでは、型ToolbarConfigurationの静的プロパティを宣言し、そのVisualStudioContributionプロパティとしてマークすることで、ツール バーを定義します。

namespace CommandParentingSample;

internal static class ExtensionCommandConfiguration
{
    [VisualStudioContribution]
    public static ToolbarConfiguration ToolBar => new("%CommandParentingSample.ToolBar.DisplayName%")
    {
        Children = new[]
        {
            ToolbarChild.Command<SampleCommand>(),
        },
    };
}

Visual Studio のコントリビューション プロパティも コンパイル時定数 であり、前に説明したのと同じ制限が適用されます。

Visual Studio のコントリビューション プロパティは、別の構成プロパティを参照することもできます。 次に例を示します。

public static class MenuConfigurations
{
    [VisualStudioContribution]
    public static CommandGroupConfiguration MyCommandGroup => new(GroupPlacement.KnownPlacements.ExtensionsMenu)
    {
        Children = new GroupChild[]
        {
            GroupChild.Menu(MyMenu),
        },
    };

    [VisualStudioContribution]
    public static MenuConfiguration MyMenu => new("%MyMenu.DisplayName%")
    {
        Children = new[]
        {
            MenuChild.Command<MyCommand>(),
        },
    };
    ...

Visual Studio のコントリビューション プロパティを定義するために使用される型は、インターフェイスを IVisualStudioContributionProperty 実装し、拡張機能のビルド時に値が評価されることを文書化するために属性で CompileTimeEvaluation マークされます。

[CompileTimeEvaluation]
public sealed class DocumentTypeConfiguration : IVisualStudioContributionProperty ...

実行時にコンパイル時定数構成プロパティを参照しないことに関するガイダンスは、Visual Studio のコントリビューション プロパティにも適用されます。

Visual Studio コントリビューション プロパティに一意の識別子が必要な場合、その完全な名前 (型のフル ネームとプロパティ名を含む) は、VisualStudio.Extensibility インフラストラクチャによって識別子として使用されます。 たとえば、ここで説明するツール バー構成の一意識別子は次のようになります CommandParentingSample.ExtensionCommandConfiguration.ToolbarConfiguration