Unterstützung generischer Typen von Razor-Komponenten in ASP.NET Core

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

In diesem Artikel wird die Unterstützung generischer Typen in Razor-Komponenten beschrieben.

Wenn Sie noch nicht mit generischen Typen gearbeitet haben, sehen Sie sich unter Generische Klassen und Methoden (C#-Leitfaden) eine allgemeine Anleitungen zur Verwendung von Generika an, bevor Sie diesen Artikel lesen.

Der Beispielcode in diesem Artikel ist nur für die neueste .NET-Version in den Blazor Beispiel-Apps verfügbar.

Unterstützung generischer Typparameter

Die @typeparam-Direktive deklariert einen generischen Typparameter für die generierte Komponentenklasse:

@typeparam TItem

C#-Syntax mit where-Typeinschränkungen wird unterstützt:

@typeparam TEntity where TEntity : IEntity

Im folgenden Beispiel wird die Komponente ListItems1 generisch als TExample eingegeben, die den Typ der Auflistung ExampleList darstellt.

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

Die folgende Komponente rendert zwei ListItems1-Komponenten:

  • Zeichenfolgen- oder Integerdaten werden dem Parameter ExampleList jeder Komponente zugewiesen.
  • Für den Typparameter (TExample) jeder Komponente wird je nach Typ der zugewiesenen Daten der Typ string oder int festgelegt.

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

Weitere Informationen finden Sie unter Razor-Syntaxreferenz für ASP.NET Core. Ein Beispiel für die generische Typisierung mit Komponentenvorlagen finden Sie unter ASP.NET Core Blazor-Komponentenvorlagen.

Unterstützung für kaskadierte generische Typen

Eine Vorgängerkomponente kann mithilfe des [CascadingTypeParameter]-Attributs einen Typparameter nach Namen an Nachfolger kaskadieren. Mit diesem Attribut kann ein generischer Typrückschluss den angegebenen Typparameter automatisch bei Nachfolgern verwenden, die einen Typparameter mit demselben Namen besitzen.

Wenn @attribute [CascadingTypeParameter(...)] Sie einer Komponente hinzufügen, wird das angegebene generische Typargument automatisch von Nachfolgern verwendet, für die Folgendes gilt:

  • Sie werden als untergeordneter Inhalt für die Komponente im selben .razor Dokument geschachtelt.
  • Sie deklarieren auch einen @typeparam mit genau demselben Namen.
  • Für den Typparameter sollte kein anderer Wert explizit angegeben oder implizit abgeleitet werden. Wenn ein anderer Wert bereitgestellt oder abgeleitet wird, besitzt er Vorrang vor dem kaskadierten generischen Typ.

Beim Empfang eines kaskadierten Typparameters erhalten Komponenten den Parameterwert vom nächstgelegenen Vorgänger, der über ein [CascadingTypeParameter]-Attribut mit einem übereinstimmenden Namen verfügt. Kaskadierte generische Typparameter werden innerhalb einer bestimmten Teilstruktur überschrieben.

Der Abgleich wird nur anhand des Namens ausgeführt. Daher empfiehlt es sich, einen kaskadierten generischen Typparameter mit einem generischen Namen zu vermeiden, z. B. T oder TItem. Wenn sich ein Entwickler für die Kaskadierung eines Typparameters entscheidet, verspricht er implizit, dass der Name eindeutig genug ist, um nicht mit anderen kaskadierten Typparametern von nicht verwandten Komponenten in Konflikt zu geraten.

Generische Typen können mit einem der folgenden Ansätze mit übergeordneten Komponenten an untergeordnete Komponenten kaskadiert werden. Dies wird in den folgenden beiden Unterabschnitten veranschaulicht:

  • Legen Sie den kaskadierten generischen Typ explizit fest.
  • Leiten Sie den kaskadierten generischen Typ ab.

Die folgenden Unterabschnitte enthalten Beispiele für die vorherigen Ansätze, die die folgende ListDisplay1-Komponente verwenden. Die Komponente empfängt und rendert Listendaten, die generisch als TExample eingegeben werden. Um jede Instanz von ListDisplay1 hervorzuheben, steuert ein zusätzlicher Komponentenparameter die Farbe der 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; }
}

Explizite generische Typen auf Grundlage von Vorgängerkomponenten

In der Demonstration in diesem Abschnitt wird ein Typ explizit für TExample kaskadiert.

Hinweis

In diesem Abschnitt wird die vorige ListDisplay1-Komponente aus dem Abschnitt Unterstützung für kaskadierte generische Typen verwendet.

Die folgende ListItems2-Komponente empfängt Daten und kaskadiert einen generischen Typparameter namens TExample an ihre Nachfolgerkomponenten. In der nächsten übergeordneten Komponente wird die ListItems2-Komponente verwendet, um Listendaten mit der vorherigen ListDisplay1-Komponente anzuzeigen.

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

Die folgende übergeordnete Komponente legt den untergeordneten Inhalt (RenderFragment) von zwei ListItems2-Komponenten fest, welche die ListItems2-Typen (TExample) angeben, die an untergeordnete Komponenten kaskadiert werden. ListDisplay1-Komponenten werden mit den im Beispiel gezeigten Listenelementdaten gerendert. Zeichenfolgendaten werden mit der ersten ListItems2-Komponente verwendet, und Integerdaten werden mit der zweiten ListItems2-Komponente verwendet.

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>

Die explizite Angabe des Typs ermöglicht auch die Verwendung von kaskadierenden Werten und Parametern, um Daten für untergeordnete Komponenten bereitzustellen. Dies wird im Folgenden veranschaulicht.

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

Beim Kaskadieren der Daten im folgenden Beispiel muss der Typ für die -Komponente bereitgestellt werden.

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

Wenn mehrere generische Typen kaskadiert werden, müssen Werte für alle generischen Typen in der Menge übergeben werden. Im folgenden Beispiel sind TItem, TValue und TEdit generische GridColumn-Typen, aber die übergeordnete Komponente, die GridColumn einfügt, gibt den TItem-Typ nicht an:

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

Im vorherigen Beispiel wird ein Kompilierzeitfehler ausgelöst, der besagt, dass der GridColumn-Komponente der Typparameter TItem fehlt. Gültiger Code gibt alle Typen an:

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

Ableiten generischer Typen auf Grundlage von Vorgängerkomponenten

In der Demonstration in diesem Abschnitt wird ein Typ kaskadiert, der für TExample abgeleitet wurde.

Hinweis

In diesem Abschnitt wird die ListDisplay-Komponente aus dem Abschnitt Unterstützung für kaskadierte generische Typen verwendet.

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

Die folgende -Komponente mit abgeleiteten kaskadierten Typen enthält verschiedene Daten, die angezeigt werden können.

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>

Die folgende -Komponente mit abgeleiteten kaskadierten Typen enthält dieselben Daten, die angezeigt werden können. Im folgenden Beispiel werden die Daten direkt den Komponenten zugewiesen.

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