CascadingTypeParameterとジェネリック型制約
.NET 6のBlazorでは新機能としてCascadingTypeParameterが追加されました。これをジェネリック型制約と組み合わせて使うとビルドエラーが発生します。
CascadingTypeParameterとは
[CascadingTypeParameter]は親コンポーネントから子コンポーネントへ型パラメータを受け継がせる機能です。パラメータは[CascadingParameter]で受け継がせられましたが、その型バージョンです。ASP.NET Core 6.0以降で使えます。
以下に簡単な例を示します。ListコンポーネントからListElementコンポーネントへ「TListElement」を受け継がせています。
@attribute [CascadingTypeParameter(nameof(TListElement))]
@typeparam TListElement
<ul>
@ChildContent
</ul>
@code {
[Parameter] public RenderFragment ChildContent { get; set; } = default!;
}
@typeparam TListElement
<li>
ListElementのTListElementは @typeof(TListElement) です。
</li>
<List TListElement="float">
@* 型を指定しなくてもfloatが受け継がれます。 *@
<ListElement/>
<ListElement/>
@* 型を指定すると指定した型が優先されます。 *@
<ListElement TListElement="int"/>
</List>
@*
これを実行すると
ListElementのTListElementは System.Single です。
ListElementのTListElementは System.Single です。
ListElementのTListElementは System.Int32 です。
と表示されます。
*@
型制約を使うとエラー
ここでTListElementに型制約を使ってみます。具体的には「@typeparam TListElement」を「@typeparam TListElement where TListElement : struct」にします。
すると「‘class’、’struct’、’unmanaged’、’notnull’、’default’ の制約を組み合わせたり、複製したりすることはできません。これらは制約リストの最初に指定する必要があります。」というコンパイルエラーが発生します。
エラーメッセージはまちまちですが、struct以外のどんな制約を入れてもジェネリック型の何かがおかしいと主張してきます。
これはrazorコンパイラのバグによるものらしいです。CascadingTypeParameterは型制約と一緒に使ってはいけません。.NET7ではすでに修正されており、プレビュー版を使うとちゃんと動作します。
.NET6では放置されたままissueがクローズされていますが直さないのでしょうか。LTSとは何だったのか。よく分かっていませんが、修正が期待できるのはセキュリティ上の問題だけと思った方がいいのかもしれません。
razorの型制約には型推論が動かないバグなどもあるようで、たまに意味不明なバグメッセージに遭遇して時間を無駄にすることがあります。ジェネリック型のエラーはコードではなくコンパイラを疑ってもいいです。