supporto del tipo generico del componente core Razor ASP.NET

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Questo articolo descrive il supporto dei tipi generici nei Razor componenti.

Se non si ha familiarità con i tipi generici, vedere Classi e metodi generici (Guida per C#) per indicazioni generali sull'uso dei generics prima di leggere questo articolo.

Il codice di esempio in questo articolo è disponibile solo per la versione .NET più recente nelle Blazor app di esempio.

Supporto dei parametri di tipo generico

La direttiva @typeparam dichiara un parametro di tipo generico per la classe del componente generata:

@typeparam TItem

La sintassi C# con vincoli di tipo where è supportata:

@typeparam TEntity where TEntity : IEntity

Nell'esempio seguente il ListItems1 componente viene tipizzato in modo generico come TExample, che rappresenta il tipo della ExampleList raccolta.

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

Il componente seguente esegue il rendering di due ListItems1 componenti:

  • I dati di tipo stringa o integer vengono assegnati al parametro ExampleList di ogni componente.
  • Il tipo string o int corrispondente al tipo dei dati assegnati viene impostato per il parametro di tipo (TExample) di ogni componente.

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

Per altre informazioni, vedere Informazioni di riferimento sulla sintassi di Razor per ASP.NET Core. Per un esempio di tipizzazione generica con componenti basati su modelli, vedere Componenti basati su modelli di ASP.NET Core Blazor.

Supporto dei tipi generici propagati

Un componente predecessore può propagare ai discendenti un parametro di tipo in base al nome usando l'attributo[CascadingTypeParameter]. Questo attributo consente a un'inferenza del tipo generico di usare automaticamente il parametro di tipo specificato con i discendenti che hanno un parametro di tipo con lo stesso nome.

Aggiungendo @attribute [CascadingTypeParameter(...)] a un componente, l'argomento di tipo generico specificato viene usato automaticamente dai discendenti che:

  • Sono annidati come contenuto figlio per il componente nello stesso documento .razor.
  • Dichiarano anche un elemento @typeparam con lo stesso nome.
  • Non hanno un altro valore specificato in modo esplicito o dedotto in modo implicito per il parametro di tipo. Se un altro valore viene fornito o dedotto, ha la precedenza sul tipo generico propagato.

Quando si riceve un parametro di tipo a catena, i componenti ottengono il valore del parametro dal predecessore più vicino con un [CascadingTypeParameter] attributo con un nome corrispondente. I parametri di tipo generico propagati vengono sottoposti a override all'interno di un determinato sottoalbero.

La corrispondenza viene stabilita solo in base al nome. È quindi consigliabile evitare un parametro di tipo generico propagato con un nome generico, ad esempio T o TItem. Se uno sviluppatore opta per la propagazione di un parametro di tipo, promette implicitamente che il nome sia abbastanza univoco da non entrare in conflitto con altri parametri di tipo propagati da componenti non correlati.

I tipi generici possono essere propagati ai componenti figlio con uno degli approcci seguenti per i componenti predecessori (padre), illustrati nelle due sezioni secondarie seguenti:

  • Impostare in modo esplicito il tipo generico propagato.
  • Dedurre il tipo generico propagato.

Le sottosezioni seguenti forniscono esempi degli approcci precedenti usando il componente seguente ListDisplay1 . Il componente riceve ed esegue il rendering dei dati dell'elenco digitati in modo generico come TExample. Per far risaltare ogni istanza di ListDisplay1 , un parametro aggiuntivo del componente controlla il colore dell'elenco.

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

Tipi generici espliciti basati sui componenti predecessori

La dimostrazione di questa sezione propaga in modo esplicito un tipo per TExample.

Nota

Questa sezione usa il componente precedente ListDisplay1 nella sezione Supporto dei tipi generici cascaded.

Il componente ListItems2 seguente riceve dati e propaga un parametro di tipo generico denominato TExample ai componenti discendenti. Nel componente padre successivo, il componente ListItems2 viene usato per visualizzare i dati dell'elenco con il componente ListDisplay1 precedente.

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

Il componente padre seguente imposta il contenuto figlio (RenderFragment) di due ListItems2 componenti che specificano i ListItems2 tipi (TExample), che vengono propagati ai componenti figlio. Il rendering dei componenti ListDisplay1 viene eseguito con i dati delle voci dell'elenco mostrati nell'esempio. I dati di tipo stringa vengono usati con il primo componente ListItems2 e i dati di tipo integer vengono usati con il secondo componente 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>

Specificare in modo esplicito il tipo consente anche l'uso della propagazione di valori e parametri per fornire i dati ai componenti figlio, come illustrato nella dimostrazione seguente.

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

Quando si propagano i dati nell'esempio seguente, deve essere fornito il tipo al componente .

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

Quando più tipi generici vengono propagati, è necessario passare i valori per tutti i tipi generici del set. Nell'esempio seguente TItem, TValue e TEdit sono GridColumn tipi generici, ma il componente padre che inserisce GridColumn non specifica il tipo TItem:

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

L'esempio precedente genera un errore in fase di compilazione perché al componente GridColumn manca il parametro di tipo TItem. Il codice valido specifica tutti i tipi:

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

Dedurre i tipi generici in base ai componenti predecessori

La dimostrazione di questa sezione propaga un tipo dedotto per TExample.

Nota

Questa sezione usa il ListDisplay componente nella sezione Supporto dei tipi generici cascaded.

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

Il componente seguente con i tipi propagati dedotti fornisce dati diversi per la visualizzazione.

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>

Il componente seguente con i tipi propagati dedotti fornisce gli stessi dati per la visualizzazione. L'esempio seguente assegna direttamente i dati ai componenti.

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