|
検索
タグ
ASP.NET
.NET
ASP.NET MVC
F#
Visual Studio
Azure
ASP.NET Core
ライトニングトーク
Plone
Selenium
AJAX
C#
jQuery
JavaScript
SQL Server
ADO.NET Entity Framework
WebMatrix
LINQ
EFCore
TypeScript
カテゴリ
最新の記事
最新のコメント
記事ランキング
最新のトラックバック
以前の記事
2026年 01月 2025年 12月 2025年 11月 2025年 10月 2025年 09月 2025年 08月 2025年 07月 2025年 06月 2025年 05月 2025年 04月 2025年 03月 2025年 02月 2024年 12月 2024年 11月 2024年 10月 2024年 09月 2024年 08月 2024年 04月 2024年 03月 2024年 02月 2024年 01月 2023年 12月 2023年 11月 2023年 10月 2023年 09月 2023年 08月 2023年 07月 2023年 06月 2023年 05月 2023年 04月 2023年 03月 2023年 02月 2023年 01月 2022年 12月 2022年 11月 2022年 10月 2022年 09月 2022年 08月 2022年 07月 2022年 06月 2022年 05月 2022年 04月 2022年 03月 2022年 02月 2022年 01月 2021年 12月 2021年 11月 2021年 10月 2021年 09月 2021年 08月 2021年 07月 2021年 06月 2021年 05月 2021年 04月 2021年 03月 2021年 02月 2021年 01月 2020年 12月 2020年 11月 2020年 10月 2020年 09月 2020年 08月 2020年 07月 2020年 06月 2020年 05月 2020年 04月 2020年 03月 2020年 02月 2020年 01月 2019年 12月 2019年 11月 2019年 10月 2019年 09月 2019年 08月 2019年 07月 2019年 06月 2019年 05月 2019年 04月 2019年 03月 2019年 02月 2019年 01月 2018年 12月 2018年 11月 2018年 10月 2018年 09月 2018年 08月 2018年 07月 2018年 06月 2018年 05月 2018年 04月 2018年 03月 2018年 02月 2018年 01月 2017年 12月 2017年 11月 2017年 10月 2017年 09月 2017年 08月 2017年 07月 2017年 06月 2017年 05月 2017年 04月 2017年 02月 2017年 01月 2016年 12月 2016年 11月 2016年 10月 2016年 09月 2016年 08月 2016年 07月 2016年 06月 2016年 05月 2016年 04月 2016年 03月 2016年 02月 2016年 01月 2015年 12月 2015年 11月 2015年 10月 2015年 09月 2015年 08月 2015年 07月 2015年 05月 2015年 04月 2015年 03月 2015年 02月 2015年 01月 2014年 12月 2014年 11月 2014年 10月 2014年 09月 2014年 08月 2014年 06月 2014年 04月 2014年 03月 2014年 02月 2014年 01月 2013年 12月 2013年 10月 2013年 09月 2013年 08月 2013年 07月 2013年 06月 2013年 05月 2013年 04月 2013年 03月 2013年 02月 2013年 01月 2012年 12月 2012年 11月 2012年 10月 2012年 09月 2012年 08月 2012年 07月 2012年 06月 2012年 05月 2012年 04月 2012年 03月 2012年 02月 2012年 01月 2011年 12月 2011年 11月 2011年 10月 2011年 09月 2011年 08月 2011年 07月 2011年 06月 2011年 05月 2011年 04月 2011年 03月 2011年 02月 2011年 01月 2010年 12月 2010年 11月 2010年 10月 2010年 09月 2010年 08月 2010年 07月 2010年 06月 2010年 05月 2010年 04月 2010年 03月 2010年 02月 2010年 01月 2009年 12月 2009年 10月 2009年 09月 2009年 07月 2009年 06月 2009年 05月 2009年 04月 2009年 03月 2009年 02月 2009年 01月 2008年 12月 2008年 11月 2008年 10月 2008年 09月 2008年 08月 2008年 07月 2008年 06月 2008年 05月 2008年 04月 2008年 03月 2008年 02月 2008年 01月 2007年 12月 2007年 11月 2007年 04月 2007年 03月 2007年 02月 2007年 01月 2006年 11月 2006年 10月 2006年 09月 2006年 08月 2006年 07月 |
2021年 04月 30日
C# でのプログラミングの話。 昨今の C# プログラミングでは、オブジェクトの JSON 文字列へのシリアル化/逆シリアル化を行なうには、System.Text.Json アセンブリおよび同名の名前空間の、JsonSerializer クラスを活用することも多いと思われる。 そして他方、昨今の C# プログラミングでは、オブジェクトのプロパティは、なるべく変更不可・読み取り専用で実装することも多くなったのではないだろうか。 例えば次のような C# クラスである。 さて、このように読み取り専用プロパティを持つ、コンストラクタでのみ各プロパティ値を初期化するようなクラスであっても、System.Text.Json 名前空間の JsonSerializer クラスでは、ちゃんと、JSON 文字列からのクラスのインスタンスへ逆シリアル化できる。 例えばこんな感じ。 これは、JsonSerializer.Deserialize<T>() メソッドが、引数が適合するコンストラクタをちゃんと探し当てて、その引数付きコンストラクタを使ってインスタンス化してくれるからだ。 なので、コンストラクタ引数名とプロパティ名が (英字の大小違いは構わないが) 一致しない場合、例えば先の PersonClass クラスのコンストラクタ引数名が、 のようになっている場合は、JsonSerializer.Deserialize<T>() メソッド実行時に、「これは逆シリアル化できない!」となって例外が発生する。 なお、コンストラクタ引数の出現順序と、プロパティの出現順序は関係ない。 名前と型が適合するかどうかがポイントとなる。 コンストラクタが複数あると逆シリアル化できない!?さてこのような読み取り専用プロパティで構成されるクラス、希ではあるが、引数違いのコンストラクタをオーバーロード実装する場合がある。 例えば、引数無しデフォルトコンストラクタを必須とするような O/R マッパーライブラリでも同じクラスを使いたい場合、などだ。 では試しに、先の PersonClass に引数無しデフォルトコンストラクタを追加したとしよう。 すると、例外こそ発生しないものの、先のコードの実行結果としては、期待どおりには逆シリアル化されなくなってしまう (下記例)。 さらには、引数無しデフォルトコンストラクタではなく、引数は持つがシグネチャ違いのコンストラクタオーバーロードがある場合は、JsonSerializer.Deserialize<T>() メソッド実行時に例外が発生するようになる。 なお、引数無しデフォルトコンストラクタがある限りは、例外は発生しなくなるようだが、いずれにしても、複数オーバーロードバージョンのコンストラクタがあると、期待どおりの逆シリアル化はされなくなるようだ。 [JsonConstructor] 属性で解決このように、JsonSerializer.Deserialize<T>() メソッドでは、複数のコンストラクタがある場合、逆シリアル化に最適なコンストラクタの用意があっても、引数無しデフォルトコンストラクタを最優先に使ってオブジェクトをインスタンス化したあとに、そのオブジェクトのプロパティを設定していくようだ。 そして複数のコンストラクタがあるものの、引数無しデフォルトコンストラクタがない場合は、どのコンストラクタを選ぶかを自動では判断せずに諦めるようである。 このように、せっかく逆シリアル化に最適なコンストラクタの用意があっても、複数のコンストラクタがあるせいで、JsonSerializer.Deserialize<T>() メソッドは自動ではその最適なコンストラクタを使うようにはなっていない。 このようなシナリオの場合は、開発者が明示的に、その "逆シリアル化に最適なコンストラクタ" に [JsonConstructor] 属性を付けてやる必要がある。 こうすることで JsonSerializer.Deserialize<T>() メソッドは、引数無しデフォルトコンストラクタよりも優先して、[JsonConstructor] 属性で修飾されたコンストラクタを使ってオブジェクトをインスタンス化するようになり、期待どおりの逆シリアル化が実現できる。 補足1 - 初期化専用セッターを持つプロパティの場合先の例では読み取り専用プロパティについての話であったが、これが初期化専用セッター (init-only セッター) を持つプロパティだったらどうなるか? これはちゃんと期待どおりの逆シリアル化がされる。 ただし、使われるコンストラクタはあくまでも引数無しデフォルトコンストラクタ。 補足2 - プライベートセッターを持つプロパティの場合ではプライベートセッターの場合はどうか? この場合は、期待どおりの逆シリアル化にはならない。 各プロパティは、引数無しデフォルトコンストラクタでインスタンス化されたときの初期値のままである。 この場合は、前述のように、適切なコンストラクタオーバーロードの実装、およびそのコンストラクタを [JsonConstructor] 属性で修飾、という手立てで対処するのがよい。 しかし他にも、プライベートセッターを持つプロパティに、[JsonInclude] 属性を付与する、という方法もある。 こうすると、期待どおりの逆シリアル化がされるようになる。 ただしこの場合も、使われるコンストラクタは、引数無しデフォルトコンストラクタであることに注意。 プロパティに [JsonInclude] 属性を付ける方法のほうがよいというシナリオは、自分には思いつかなかったが、このような方法があることを知っておくと、何かレアケースな実装パターンで役に立つかもしれない。 (実際、何か需要があるからこそ [JsonInclude] などという属性が用意されていることと思うので。) おわりに以上、今回は「読み取り専用プロパティを持つオブジェクト」を取り上げたが、このようなクラスの必要があった際は、レコード型 (record) の採用を検討することもお忘れなく。 [2021/05/01 追記] じんぐるさんから Twitter でコメント頂戴した。 なるほど、こういう観点からの指針決定・判断もあるということで、今後自分の方針も適宜改善していくかもしれない。
by developer-adjust
| 2021-04-30 21:55
| .NET
|
ファン申請 |
||