ASP.NET Core の Razor コンポーネントのジェネリック型サポート

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 8 バージョンを参照してください。

この記事では、Razor コンポーネントでのジェネリック型のサポートについて説明します。

ジェネリック型を初めて使用する場合は、この記事を読む前に、ジェネリックの使用に関する一般的なガイダンスのために、「ジェネリック クラスとメソッド (C# ガイド)」を参照してください。

この記事のコード例は、Blazor サンプル アプリの最新の .NET リリースでのみ利用できます。

ジェネリック型パラメーターのサポート

@typeparam ディレクティブによって、生成されるコンポーネント クラスのジェネリック型パラメーターを宣言します。

@typeparam TItem

where 型制約を含む C# 構文がサポートされています。

@typeparam TEntity where TEntity : IEntity

次の例では、ListItems1 コンポーネントは、ExampleList コレクションの型を表す TExample としてジェネリック型指定されています。

ListItems1.razor:

@typeparam TExample

<h2>List Items 1</h2>

@if (ExampleList is not null)
{
    <ul style="color:@Color">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>

    <p>
        Type of <code>TExample</code>: @typeof(TExample)
    </p>
}

@code {
    [Parameter]
    public string? Color { get; set; }

    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }
}

次のコンポーネントでは、2 つの ListItems1 コンポーネントがレンダリングされます。

  • 文字列または整数のデータは、各コンポーネントの ExampleList パラメーターに割り当てられます。
  • 割り当てられたデータの型に一致する型 string または int が、各コンポーネントの型パラメーター (TExample) に設定されます。

Generics1.razor:

@page "/generics-1"

<PageTitle>Generics 1</PageTitle>

<h1>Generic Type Example 1</h1>

<ListItems1 Color="blue"
            ExampleList="@(new List<string> { "Item 1", "Item 2" })"
            TExample="string" />

<ListItems1 Color="red"
            ExampleList="@(new List<int> { 1, 2 })"
            TExample="int" />

詳細については、「ASP.NET Coreの Razor 構文リファレンス」を参照してください。 テンプレート化されたコンポーネントを使用したジェネリック型指定の例については、「ASP.NET Core Blazor テンプレート コンポーネント」を参照してください。

カスケードされたジェネリック型のサポート

先祖コンポーネントでは、[CascadingTypeParameter] 属性を使用して、型パラメーターを名前で子孫にカスケードさせることができます。 この属性を使用すると、ジェネリック型の推定で、指定された型パラメーターを、同じ名前の型パラメーターを持つ子孫と共に自動的に使用できます。

コンポーネントに @attribute [CascadingTypeParameter(...)] を追加すると、指定されたジェネリック型引数は、次のような子孫によって自動的に使用されます。

  • 同じ .razor ドキュメント内のコンポーネントの子コンテンツとして入れ子になっている。
  • まったく同じ名前で @typeparam を宣言している。
  • 型パラメーターに対して、明示的に指定される、または暗黙的に推定される別の値を持たないでください。 別の値を指定するか推定した場合は、カスケードされたジェネリック型よりも優先されます。

カスケードされた型パラメーターを受け取ると、コンポーネントは名前が一致する [CascadingTypeParameter] 属性を持つ最も近い先祖からパラメーター値を取得します。 カスケードされたジェネリック型パラメーターは、特定のサブツリー内でオーバーライドされます。

照合は名前によってのみ実行されます。 そのため、TTItem などの一般的な名前を持つカスケードされたジェネリック型パラメーターは使用しないことをお勧めします。 開発者が型パラメーターをカスケードすることを選択した場合、関連性のないコンポーネントからの別のカスケードされた型パラメーターと競合しないほど十分に名前が固有であることが暗黙的に期待されます。

ジェネリック型は、先祖 (親) コンポーネントに対する以下のいずれかのアプローチで子コンポーネントにカスケードできます。これについては以下の 2 つのサブセクションで例を示します。

  • カスケードされたジェネリック型を明示的に設定します。
  • カスケードされたジェネリック型を推定します。

以下のサブセクションでは、次の ListDisplay1 コンポーネントを使用する上記のアプローチの例を示します。 このコンポーネントは、TExample としてジェネリック型指定されたリスト データを受け取ってレンダリングします。 ListDisplay1 の各インスタンスを目立たせるために、追加のコンポーネント パラメーターがリストの色を制御します。

ListDisplay1.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:@Color">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public string? Color { get; set; }

    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }
}

先祖コンポーネントに基づいた明示的なジェネリック型

このセクションのデモでは、TExample の型を明示的にカスケードします。

Note

このセクションでは、「カスケードされたジェネリック型のサポート」セクションで前述の ListDisplay1 コンポーネントを使用します。

次の ListItems2 コンポーネントでは、データを受け取り、TExample という名前のジェネリック型パラメーターをその子孫コンポーネントにカスケードします。 次の親コンポーネントでは、ListItems2 コンポーネントを使用して、前の ListDisplay1 コンポーネントでリスト データを表示します。

ListItems2.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Items 2</h2>

@ChildContent

<p>
    Type of <code>TExample</code>: @typeof(TExample)
</p>

@code {
    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

次の親コンポーネントは、子コンポーネントにカスケードされる ListItems2 型 (TExample) を指定する 2 つの ListItems2 コンポーネントの子コンテンツ (RenderFragment) を設定します。 ListDisplay1 コンポーネントは、例に示されているリスト項目データと一緒にレンダリングされます。 文字列データは 1 番目の ListItems2 コンポーネントで使用され、整数データは 2 番目のListItems2 コンポーネントで使用されます。

Generics2.razor:

@page "/generics-2"

<PageTitle>Generics 2</PageTitle>

<h1>Generic Type Example 2</h1>

<ListItems2 TExample="string">
    <ListDisplay1 Color="blue" 
                  ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
    <ListDisplay1 Color="red" 
                  ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListItems2>

<ListItems2 TExample="int">
    <ListDisplay1 Color="blue" 
                  ExampleList="@(new List<int> { 1, 2 })" />
    <ListDisplay1 Color="red" 
                  ExampleList="@(new List<int> { 3, 4 })" />
</ListItems2>

次のデモに示すように、型を明示的に指定すると、値とパラメーターのカスケードを使用して子コンポーネントにデータを提供することもできます。

ListDisplay2.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:@Color">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public string? Color { get; set; }

    [CascadingParameter]
    protected IEnumerable<TExample>? ExampleList { get; set; }
}

ListItems3.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Items 3</h2>

@ChildContent

@if (ExampleList is not null)
{
    <ul style="color:green">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>

    <p>
        Type of <code>TExample</code>: @typeof(TExample)
    </p>
}

@code {
    [CascadingParameter]
    protected IEnumerable<TExample>? ExampleList { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

次の例のデータをカスケードする場合は、型を コンポーネントに提供する必要があります。

Generics3.razor:

@page "/generics-3"

<PageTitle>Generics 3</PageTitle>

<h1>Generic Type Example 3</h1>

<CascadingValue Value="stringData">
    <ListItems3 TExample="string">
        <ListDisplay2 Color="blue" />
        <ListDisplay2 Color="red" />
    </ListItems3>
</CascadingValue>

<CascadingValue Value="integerData">
    <ListItems3 TExample="int">
        <ListDisplay2 Color="blue" />
        <ListDisplay2 Color="red" />
    </ListItems3>
</CascadingValue>

@code {
    private List<string> stringData = new() { "Item 1", "Item 2" };
    private List<int> integerData = new() { 1, 2 };
}

複数のジェネリック型をカスケードする場合は、セット内のすべてのジェネリック型の値を渡す必要があります。 次の例では、TItemTValueTEditGridColumn ジェネリック型ですが、GridColumn を配置する親コンポーネントは TItem 型を指定していません。

<GridColumn TValue="string" TEdit="TextEdit" />

上記の例では、GridColumn コンポーネントに TItem 型パラメーターがないというコンパイル時のエラーが生成されます。 有効なコードでは、すべての型を指定します。

<GridColumn TValue="string" TEdit="TextEdit" TItem="User" />

先祖コンポーネントに基づいてジェネリック型を推定する

このセクションのデモでは、TExample に対して推定された型をカスケードします。

Note

このセクションでは、「カスケードされたジェネリック型のサポート」セクションの ListDisplay コンポーネントを使用します。

ListItems4.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Items 4</h2>

@ChildContent

@if (ExampleList is not null)
{
    <ul style="color:green">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>

    <p>
        Type of <code>TExample</code>: @typeof(TExample)
    </p>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

カスケードされた型が推定される次の コンポーネントは、表示用に異なるデータを提供します。

Generics4.razor:

@page "/generics-4"

<PageTitle>Generics 4</PageTitle>

<h1>Generic Type Example 4</h1>

<ListItems4 ExampleList="@(new List<string> { "Item 5", "Item 6" })">
    <ListDisplay1 Color="blue" 
                  ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
    <ListDisplay1 Color="red" 
                  ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListItems4>

<ListItems4 ExampleList="@(new List<int> { 5, 6 })">
    <ListDisplay1 Color="blue" 
                  ExampleList="@(new List<int> { 1, 2 })" />
    <ListDisplay1 Color="red" 
                  ExampleList="@(new List<int> { 3, 4 })" />
</ListItems4>

カスケードされた型が推定される次の コンポーネントは、表示用に同じデータを提供します。 次の例では、データをコンポーネントに直接割り当てます。

Generics5.razor:

@page "/generics-5"

<PageTitle>Generics 5</PageTitle>

<h1>Generic Type Example 5</h1>

<ListItems4 ExampleList="stringData">
    <ListDisplay1 Color="blue" ExampleList="stringData" />
    <ListDisplay1 Color="red" ExampleList="stringData" />
</ListItems4>

<ListItems4 ExampleList="integerData">
    <ListDisplay1 Color="blue" ExampleList="integerData" />
    <ListDisplay1 Color="red" ExampleList="integerData" />
</ListItems4>

@code {
    private List<string> stringData = new() { "Item 1", "Item 2" };
    private List<int> integerData = new() { 1, 2 };
}