Prise en charge des types génériques de composants Razor ASP.NET Core
Remarque
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 8 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la Stratégie de prise en charge de .NET et .NET Core. Pour la version actuelle, consultez la version .NET 8 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 8 de cet article.
Cet article décrit la prise en charge des types génériques dans les composants Razor.
Si vous débutez avec les types génériques, consultez Classes et méthodes génériques (guide C#) pour obtenir des conseils généraux sur l’utilisation des génériques avant de lire cet article.
L’exemple de code de cet article est disponible uniquement pour la dernière version de .NET dans les exemples d’applications Blazor.
Prise en charge des paramètres de type générique
La directive @typeparam
déclare un paramètre de type générique pour la classe de composant générée :
@typeparam TItem
La syntaxe C# avec les contraintes de type where
est prise en charge :
@typeparam TEntity where TEntity : IEntity
Un type générique RenderFragment est pris en charge, mais les composants ne sont pas pris en charge comme le type générique. Pour afficher les composants par type, envisagez d’utiliser un DynamicComponent. Pour plus d’informations, consultez Composants ASP.NET Core Razor rendus dynamiquement.
Dans l’exemple suivant, le composant ListItems1
est de type générique TExample
, ce qui représente le type de la collection ExampleList
.
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; }
}
@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; }
}
Le composant suivant génère le rendu de deux ListItems1
composants :
- Des données de chaîne ou d’entier sont affectées au paramètre
ExampleList
de chaque composant. - Le type
string
ouint
qui correspond au type des données affectées est défini pour le paramètre de type (TExample
) de chaque composant.
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" />
@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" />
Pour en savoir plus, consultez les informations de référence sur la syntaxe Razor pour ASP.NET Core. Pour obtenir un exemple de type générique avec des composants basés sur un modèle, consultez les composants basés sur un modèle Blazor ASP.NET Core.
Prise en charge du type générique en cascade
Un composant ancêtre peut passer en cascade un paramètre de type par nom à des descendants avec l’attribut [CascadingTypeParameter]
. Cet attribut permet à une inférence de type générique d’utiliser automatiquement le paramètre de type spécifié avec les descendants qui ont un paramètre de type portant le même nom.
Si vous ajoutez @attribute [CascadingTypeParameter(...)]
à un composant, l’argument de type générique spécifié est automatiquement utilisé par les descendants qui :
- Sont imbriqués comme contenu enfant pour le composant dans le même document
.razor
. - Déclarent également un
@typeparam
avec le même nom. - N’ont pas d’autre valeur explicitement fournie ou implicitement déduite pour le paramètre de type. Si une autre valeur est fournie ou déduite, elle a priorité sur le type générique en cascade.
Lors de la réception d’un paramètre de type cascade, les composants obtiennent la valeur de paramètre de l’ancêtre le plus proche qui a un attribut [CascadingTypeParameter]
avec un nom correspondant. Les paramètres de type générique en cascade sont remplacés dans une sous-arborescence particulière.
La correspondance n’est effectuée que par nom. Nous vous recommandons donc d’éviter d’utiliser un paramètre de type générique en cascade avec un nom générique, par exemple T
ou TItem
. Si un développeur choisit de passer en cascade un paramètre de type, il promet implicitement que son nom est suffisamment unique pour ne pas entrer en conflit avec d’autres paramètres de type en cascade provenant de composants non liés.
Les types génériques peuvent être transmis en cascade à des composants enfants selon l’une des approches suivantes avec des composants ancêtres (parents). Ces approches sont présentées dans les deux sous-sections suivantes :
- Définir explicitement le type générique en cascade.
- Déduire explicitement le type générique en cascade.
Les sous-sections suivantes fournissent des exemples des approches précédentes en utilisant le composant ListDisplay1
suivant. Le composant reçoit et affiche les données de liste de type générique en tant que TExample
. Pour que chaque instance de ListDisplay1
se distingue, un paramètre de composant supplémentaire contrôle la couleur de la liste.
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; }
}
@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; }
}
Types génériques explicites basés sur des composants ancêtres
La démonstration dans cette section passe en cascade un type de manière explicite pour TExample
.
Remarque
Cette section utilise le composant ListDisplay1
précédent de la section Prise en charge des types génériques en cascade.
Le composant ListItems2
suivant reçoit des données et passe en cascade un paramètre de type générique nommé TExample
à ses composants descendants. Dans le composant parent à venir, le composant ListItems2
est utilisé pour afficher des données de liste avec le composant ListDisplay1
précédent.
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; }
}
@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; }
}
Le composant parent suivant définit le contenu enfant (RenderFragment) de deux ListItems2
composants spécifiant les ListItems2
types (TExample
), qui sont passés en cascade aux composants enfants. Les composants ListDisplay1
sont rendus avec les données d’élément de liste présentées dans l’exemple. Les données de type chaîne sont utilisées avec le premier composant ListItems2
tandis que les données de type int sont utilisées avec le second composant 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>
@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>
La spécification explicite du type permet également l’utilisation de valeurs et paramètres en cascade pour fournir des données aux composants enfants, comme le montre la démonstration suivante.
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; }
}
@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; }
}
@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; }
}
Lors du passage en cascade des données dans l’exemple suivant, le type doit être fourni au composant .
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 };
}
@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 };
}
Quand plusieurs types génériques sont passés en cascade, les valeurs de tous les types génériques de l’ensemble doivent être passées. Dans l’exemple suivant, TItem
, TValue
et TEdit
sont des types génériques GridColumn
, mais le composant parent qui place GridColumn
ne spécifie pas le type TItem
:
<GridColumn TValue="string" TEdit="TextEdit" />
L’exemple précédent génère une erreur de compilation indiquant que le composant GridColumn
ne contient pas le paramètre de type TItem
. Le code valide spécifie tous les types :
<GridColumn TValue="string" TEdit="TextEdit" TItem="User" />
Déduire les types génériques en fonction de composants ancêtres
La démonstration dans cette section passe en cascade un type déduit pour TExample
.
Remarque
Cette section utilise le composant ListDisplay
de la section Prise en charge des types génériques en cascade.
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; }
}
@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; }
}
Le composant suivant avec des types en cascade déduits fournit des données différentes à des fins d’affichage.
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>
@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>
Le composant suivant avec des types en cascade déduits fournit les mêmes données à des fins d’affichage. L’exemple suivant affecte directement les données aux composants.
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 };
}
@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 };
}