obsługa typu ogólnego składnika ASP.NET Core Razor

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

W tym artykule opisano obsługę typów ogólnych w Razor składnikach.

Jeśli dopiero zaczynasz korzystać z typów ogólnych, zobacz Ogólne klasy i metody (Przewodnik języka C#), aby uzyskać ogólne wskazówki dotyczące używania typów ogólnych przed przeczytaniem tego artykułu.

Przykładowy kod w tym artykule jest dostępny tylko dla najnowszej wersji platformy .NET w przykładowych Blazor aplikacjach.

Obsługa parametrów typów ogólnych

Dyrektywa @typeparam deklaruje parametr typu ogólnego dla generowanej klasy składnika:

@typeparam TItem

Obsługiwana jest składnia języka C# z ograniczeniami typów where:

@typeparam TEntity where TEntity : IEntity

W poniższym przykładzie ListItems1 składnik jest typowo typizowane jako TExample, który reprezentuje typ ExampleList kolekcji.

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; }
}

Następujący składnik renderuje dwa ListItems1 składniki:

  • Dane będące ciągiem lub liczbą całkowitą są przypisywane do parametru ExampleList każdego składnika.
  • Dla parametru typu (TExample) każdego składnika ustawiany jest typ string lub int, który jest zgodny z typem przypisywanych danych.

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" />

Aby uzyskać więcej informacji, zobacz Dokumentacja składni aparatu Razor dla platformy ASP.NET Core. Aby uzyskać przykład używania typów ogólnych razem ze składnikami z szablonami, zobacz Składniki z szablonami na platformie ASP.NET Core Blazor.

Obsługa kaskadowych typów ogólnych

Składnik nadrzędny może przekazywać kaskadowo parametr typu według nazwy do składników podrzędnych za pomocą atrybutu [CascadingTypeParameter]. Ten atrybut umożliwia ogólne wnioskowanie o typach przez automatyczne używanie określonego parametru typu z elementami podrzędnymi, które mają parametr typu o takiej samej nazwie.

Dodanie dyrektywy @attribute [CascadingTypeParameter(...)] do składnika powoduje, że określony argument typu ogólnego jest automatycznie używany przez składniki podrzędne, które:

  • Są zagnieżdżone jako zawartość podrzędna składnika w tym samym dokumencie .razor.
  • Również deklarują atrybut @typeparam o dokładnie takiej samej nazwie.
  • Nie mają innej wartości, która jest jawnie podana lub domyślnie wywnioskowana dla parametru typu. Jeśli zostanie podana lub wywnioskowana inna wartość, ma ona pierwszeństwo przed kaskadowym typem ogólnym.

Podczas odbierania parametru typu kaskadowego składniki uzyskują wartość parametru z najbliższego przodka, który ma [CascadingTypeParameter] atrybut o pasującej nazwie. Kaskadowe parametry typów ogólnych są zastępowane w obrębie danego poddrzewa.

Dopasowywanie jest wykonywane tylko według nazwy. Dlatego zalecamy unikanie kaskadowych parametrów typu ogólnego mających nazwy ogólne, na przykład T lub TItem. Jeśli deweloper decyduje się użyć kaskadowego parametru typu, niejawnie przyjmuje zobowiązanie, że jego nazwa jest wystarczająco unikatowa, aby nie kolidować z innymi kaskadowymi parametrami typów z niepowiązanych składników.

Typy ogólne mogą być kaskadowe do składników podrzędnych z jednym z następujących podejść dla składników nadrzędnych , które przedstawiono w następujących dwóch podsekcjach:

  • Jawne ustawianie kaskadowego typu ogólnego.
  • Wywnioskowywanie kaskadowego typu ogólnego.

Poniższe podsekcje zawierają przykłady powyższych podejść przy użyciu następującego ListDisplay1 składnika. Składnik odbiera i renderuje dane listy typowo typizowane jako TExample. Aby wyróżnić każde wystąpienie ListDisplay1 , dodatkowy parametr składnika kontroluje kolor listy.

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; }
}

Jawne typy ogólne oparte na składnikach nadrzędnych

W tej sekcji przedstawiono jawne kaskadowe przekazywanie typu dla elementu TExample.

Uwaga

W tej sekcji użyto poprzedniego ListDisplay1 składnika w sekcji obsługi typu ogólnego Kaskadowo.

Poniższy składnik ListItems2 otrzymuje dane i kaskadowo przekazuje parametr typu ogólnego o nazwie TExample do swoich składników podrzędnych. W następnym składniku nadrzędnym używany jest składnik ListItems2 do wyświetlania danych listy z powyższego składnika 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; }
}

Poniższy składnik nadrzędny ustawia zawartość podrzędną (RenderFragment) dwóch ListItems2 składników określających ListItems2 typy (TExample), które są kaskadowo do składników podrzędnych. Składniki ListDisplay1 są renderowane z danymi elementów listy pokazanymi w przykładzie. Dane w postaci ciągów są używane z pierwszym składnikiem ListItems2, a dane w postaci liczb całkowitych są używane z drugim składnikiem 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>

Jawne określanie typu pozwala też używać kaskadowych wartości i parametrów w celu przekazywania danych składnikom podrzędnym, jak pokazano poniżej.

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; }
}

Podczas kaskadowego przekazywania danych w poniższym przykładzie należy podać typ dla składnika .

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 };
}

Jeśli wiele typów ogólnych jest przekazywanych kaskadowo, należy podać wartości dla wszystkich typów ogólnych w zestawie. W poniższym przykładzie TItem, TValue i TEdit są typami ogólnymi GridColumn, ale składnik nadrzędny, który umieszcza element GridColumn, nie określa typu TItem:

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

Powyższy przykład generuje błąd przy kompilacji, informujący, że w składniku GridColumn brakuje parametru typu TItem. Poprawny kod określa wszystkie typy:

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

Wnioskowanie typów ogólnych na podstawie składników nadrzędnych

W tej sekcji przedstawiono kaskadowe przekazywanie typu wywnioskowanego dla elementu TExample.

Uwaga

W tej sekcji użyto ListDisplay składnika w sekcji Obsługa typów ogólnych Kaskadowo.

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; }
}

Poniższy składnik z wywnioskowanymi typami kaskadowymi udostępnia różne dane do wyświetlenia.

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>

Poniższy składnik z wywnioskowanymi typami kaskadowymi udostępnia takie same dane do wyświetlenia. W poniższym przykładzie dane są przypisywane bezpośrednio do składników.

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 };
}