Blazor - 親コンポーネントのフィールド変数を、子コンポーネントの文字列型パラメーターに渡すときに、@ を付け忘れて起きたこと

## 親のフィールド変数を、子のパラメーターに渡す

Blazor では、コンポーネントのパラメーターとして、様々な型のパラメーターを設け、親コンポーネントから子コンポーネントへ、それらパラメーターに値を渡すことができます。

例えば次のような Razor コンポーネント "Child.razor" があったとして、

```cs
@* Child.razor *@
<dl>
    <dt>Number</dt> <dd>@Number</dd>
    <dt>Date</dt>   <dd>@Date</dd>
    <dt>Type</dt>   <dd>@(Type?.Name)</dd>
    <dt>Object</dt> <dd>@(Object?.ToString())</dd>
</dl>

@code {
    [Parameter]
    public int Number { get; set; }

    [Parameter]
    public DateTime Date { get; set; }

    [Parameter]
    public Type? Type { get; set; }

    [Parameter]
    public object? Object { get; set; }
}
```

この Child コンポーネントを使う側の、親の Razor コンポーネントでは以下のようにして、Child コンポーネントのパラメーターに、自身のもつフィールド変数を渡すことができます。

```cs
<Child Number="_number" 
       Date="_date" 
       Type="_type" 
       Object="_obj">
</Child>

@code {
    private int _number = 123;

    private DateTime _date = DateTime.Now;

    private Type _type = typeof(Child);

    private object? _obj = new { Foo = "Bar" };
}
```

上記コンポーネントのレンダリング結果は、ブラウザ画面上、以下のように表示されることでしょう。

```
Number 123
Date   12/31/2023 2:53:03 PM
Type   Child
Object { Foo = Bar }
```

## パラメーターの型が string の場合

ここまでの例では、これら子コンポーネント (Child.razor) のパラメーターには、string 型のパラメーターがありませんでした。では次に、string 型のパラメーターを設けて、同じように、親コンポーネントに設けた string 型フィールド変数を渡してみましょう。

子コンポーネント (Child.razor) に以下のように string 型のパラメーターを設け、

```cs
@* Child.razor *@
<dl>
    <dt>Text</dt> <dd>@Text</dd>
    ...
</dl>

@code {
    [Parameter]
    public string? Text { get; set; }
    ...
}
```

親コンポーネントは以下のように実装します。

```cs
<Child Text="_text" 
       ...>
</Child>

@code {
    private string _text = "Hello, World!";
    ...
}
```

さて、これで実行してみます。ブラウザ画面上には以下のように表示されることを期待します。

```
Text   Hello, World!
...
```

ところが実際に表示されるのは以下のとおりです。

```
Text   _text
...
```

他のパラメーターは期待どおりに動作しているのに、この `public string Text { get; set; }` パラメーターへの指定だけ、渡している親コンポーネントのフィールド変数名が割り当たってしまっています。

## string 型のパラメーターに、フィールド変数名が渡ってしまう理由

これはどうも、Blazor、というか、Razor コンパイラの仕様に起因するようです。どうやら、Razor コンパイラは親切に、string 型のパラメーターへのマークアップは特別扱いして、文字列リテラルを渡しているものと解釈されるっぽいです。

Visual Studio Code 上でも、構文ハイライト機能によって、`Text` パラメーターへの引き渡しが文字列リテラルとして認識されていることがわかります (下図参照。C# の変数を示す水色ではなく、文字列リテラルを示す赤い色で表示されていることがわかります)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/2f97f5e6-8d82-440c-fd7f-170b011d87ad.png)


この仕様のおかげで、例えば、コンポーネントの文字列型パラメーターに、インラインで文字列リテラルを指定する場合に、素の HTML のマークアップと同じように記述できるわけですね (下記例)。

```html
<Child Text="Hello, World!" />
```

これが、このような string 型パラメーターへの特別扱いがなかったら、以下のように記述する必要が発生することでしょう。

```html
<Child Text="\"Hello, World!\"" />
```

もっとも、これはこれで一貫性もあり、必ずしも悪いわけではないとも感じます。ですが Razor 構文の設計においては、素の HTML っぽい書き味を求めたのかもしれませんね。

## `@` を付けて C# コードであることを明示

さておき、この場合は、`@` を付けて、C# コードであることを明示することで、期待どおりに動作するよう修正することができます。以下のように string 型のパラメーターへのフィールド変数の引き渡しには `@` を付けることで、

```cs
<Child Text="@_text"
       Number="_number"
       Date="_date"
       Type="_type"
       Object="_obj">
</Child>
...
```

以下のように期待した画面表示を得られます。

```
Text   Hello, World!
Number 123
Date   12/31/2023 2:53:03 PM
Type   Child
Object { Foo = Bar }
```

なお、string 以外の型のパラメーターについては、`@` を付けなくても期待した動作でしたが、逆に `@` を付けても、挙動は変わりません (下記例)。

```cs
<Child Text="@_text"
       Number="@_number"
       Date="@_date"
       Type="@_type"
       Object="@_obj">
</Child>
...
```
なので、とりあえず何も考えずに、親のフィールド変数を子のパラメーターに渡すときは常に `@` を付けまくる、としてもよいのかもしれません。実際、.NET SDK 標準のプロジェクトテンプレートから生成される Blazor プログラムのソースコードにおいても、`@` は必須ではないパラメーターに対して、`@` 付きで値指定しているようです。

## まとめ

ということで、親の Razor コンポーネントのフィールド変数やプロパティ、メソッド呼び出しを、子コンポーネントのパラメーターに渡す際は、そのパラメーターが string 型なら `@` 指定は必須ですよね、あるいはその逆に、あらゆるパラメーターの指定に `@` をつけたほうがいいのでしょうか、という話でした。

自分は、このシナリオにおいて、`@` をすべてつけたほうがいいのか、string 型のところだけ `@` 付けるのか、判断が付きかねています。ご意見等ありましたら、コメント欄などにてお知らせください。

_Learn, Practice, Share!_