- 投稿日:2021-03-05T20:57:58+09:00
C#で画面をつくる。何でつくる?
C#で画面を作るときの選択肢について
はじめにの前に
Qiitaユーザーはzennに引越したのでしょうか。
投げ銭システムは魅力的ですよね。Qiitaランキングの載っている記事が、個々人の備忘録のレベルに下がってきている気がしています(余計なお世話)
個人的には、記事を書くハードルが下がるので、棲み分けはよいことだと思います。
はじめに
.NET 6やらWinUI 3など、いろいろな技術がリリースされる中
私の主戦場はもっぱらWinFormsです。たまにWPFも触るけど、UWPは触ったことなく
でもそろそろクロスプラットフォーム開発とかもしてみたいなー、と。というわけで、この記事では
いろいろ知りたいこと(以下)を、ググりつつ情報をまとめていくための記事です。
- UWPはユニバーサルらしいけど、WPFと何が違うの?
- Uno Platformや、COCOAで話題のXamarinの違いは何なのか?
※注意:はじめに に対する結論があるとはかぎりません
WinForm, WPF, UWPの違い
マイクロソフト公式サイトに比較表があった。
プラットフォームの比較:UWP、WPF、Windows フォーム
表を見てもよく分からない。
勝手なイメージは・・・↓
WinFormn ||||||||すごい隔たり||||||||| WPF || UPWとくにWPFとUWPの違いが分からない。
- UWPはC++/WinRT、 C++/CX
- WPFはC++/CLI (C++マネージド拡張)
C++/CLI, C++/CX, WRL, C++/WinRT違いメモ
↑記事では、C++/CLIはグルー言語として使えると書いてある。
確かにC++ネイティブをC#から呼ぶときに、ラッパーとして使ったことがある。
そのとき参考にしたサイト↓
C#から、C++の関数の実行(クラス)WinFormからWPF/UWPへの以降は大変?
たやすくはない。しかし得るものも多い。
よく見るのは、XAMLの学習コストが高いからWinFormのままにしておけという引き止め意見。UWPは"Xbox、HoloLens、IoT、または Surface Hub のサポート"らしいけど、そんなに手広くやるつもりもない。
OpenCVなんかは、OpenCvSharpに比べて参考サイトが多いから
画像処理をC++で書いてC#から使うときは、WPFのほうが障壁が少なそうなイメージ。あと綺麗な画面が比較的つくりやすい。
すでにあるWPFアプリをマテリアルデザインにするクロスプラットフォーム開発について
C-SHARP
C#だったらXamarin(Xamarin.Forms?)、Uno Platform、あと新しいMAUI
Xamarin は、.NET を使用して、iOS、Android、Windows 向けの最新で高性能なアプリケーションをビルドするためのオープンソースのプラットフォームです。
Unoプラットフォームは、UWPベースのコード(C#およびXAML)をiOS、Android、およびWebAssemblyで実行できるようにするユニバーサルWindowsプラットフォームブリッジです。
マルチプラットフォームのネイティブUI
モバイルとデスクトップ全体の複数のデバイスにデプロイ
単一のプロジェクト、単一のコードベースを使用
Xamarin.Forms進化
.NET 6つまり・・・
Xamarin.FormsはMAUIに進化した。
WebAssemblyに対応しているのはUno Platformだけ。WPFの参考記事を多数執筆されているokazukiさんによるUno Platformデモ
C# で湯婆婆を Windows アプリと WebAssembly 対応してみた(無駄に MVVM)脱線してC++
Qtがデファクトスタンダードなんですかね。
できることが多すぎる・・・Qtは、デスクトップ、組み込み、モバイル向けのクロスプラットフォームのアプリケーション開発フレームワークです。サポートされているプラットフォームには、Linux、OS X、Windows、VxWorks、QNX、Android、iOS、BlackBerry、SailfishOSなどがあります。
WebAssemblyにも対応
Qtを使って、デスクトップアプリ開発とWebAssemblyアプリ開発を同時に学ぼうさらに脱線してPython
Qt + Python = デスクトップアプリケーションを作ることもできる
結論
MAUIを使います。
Visual Studio 2019での使い方を調べる以上
参考
- 投稿日:2021-03-05T18:26:34+09:00
Serilogの設定ファイル(デスクトップアプリケーション向け)JSON版
はじめに
Serilogの設定ファイルを備忘のため残します。
デスクトップアプリケーション向けの設定内容です。ログレベル別に2種類(全レベル/Warning以上)× 出力形式別に2種類(通常のテキスト形式/JSON形式)
で合計4ファイルを出力する内容となっています。
各設定項目の意味は、コメントとして記載しています。Nugetパッケージ
インストールしたSerilog関連のNugetパッケージは以下の通りです。
- Serilog
- Serilog.Enrichers.AssemblyName
- Serilog.Enrichers.Environment
- Serilog.Enrichers.Memory
- Serilog.Enrichers.Process
- Serilog.Enrichers.Thread
- Serilog.Exceptions
- Serilog.Filters.Expressions
- 非推奨のパッケージ。.NET Core 以上であれば Serilog.Expressions
- Serilog.Formatting.Compact
- Serilog.Settings.Configuration
- Serilog.Sinks.File
設定ファイル(.json)
{ "Serilog": { "Using": [ "Serilog.Sinks.File" ], //ファイルに出力 "MinimumLevel": { //最小ログレベル "Default": "Verbose" }, "Enrich": [ //拡張 "WithThreadId", //スレッドID "WithThreadName", //スレッド名 "WithMachineName", //マシン名 "WithEnvironmentUserName", //ユーザー名 "WithProcessId", //プロセスID "WithProcessName", //プロセス名 "WithAssemblyName", //アセンブリ名 "WithAssemblyVersion", //アセンブリバージョン "WithMemoryUsage", //メモリ使用量 "WithExceptionDetails" //例外の詳細情報 ], "WriteTo": [ { //通常のテキスト形式で、全レベルのログを出力するためのLogger "Name": "Logger", "Args": { "configureLogger": { "WriteTo": [ { "Name": "File", "Args": { //ログファイルパス "path": "Logs/All/Default/all.log", //ログファイルのフォーマット "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} | [{Level:u3}] | {ThreadId:00}:{ThreadName} | {ProcessId:00}:{ProcessName} | {Message:lj} | {AssemblyName} | {AssemblyVersion} | {MachineName} | {EnvironmentUserName} | {MemoryUsage} B | {NewLine}{Exception}", //1日毎にロールする "rollingInterval": "Day", //直近の7ファイルを保持する(デフォルトでは直近の31ファイル) "retainedFileCountLimit": 7, //ファイルサイズの制限を削除(デフォルトでは1GB) "fileSizeLimitBytes": null, //書き込みをバッファリングする "buffered": true } } ] } } }, { //JSON形式で、全レベルの構造化ログを出力するためのLogger "Name": "Logger", "Args": { "configureLogger": { "WriteTo": [ { "Name": "File", "Args": { "path": "Logs/All/Compact/all_compact.json", //JsonFormatter "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact", "rollingInterval": "Day", "retainedFileCountLimit": 7, "fileSizeLimitBytes": null, "buffered": true } } ] } } }, { //通常のテキスト形式で、Warning以上のログを出力するためのLogger "Name": "Logger", "Args": { "configureLogger": { "Filter": [ { "Name": "ByIncludingOnly", "Args": { "expression": "(@Level = 'Error' or @Level = 'Fatal' or @Level = 'Warning')" } } ], "WriteTo": [ { "Name": "File", "Args": { "path": "Logs/Error/Default/error.log", "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} | [{Level:u3}] | {ThreadId:00}:{ThreadName} | {ProcessId:00}:{ProcessName} | {Message:lj} | {AssemblyName} | {AssemblyVersion} | {MachineName} | {EnvironmentUserName} | {MemoryUsage} B | {NewLine}{Exception}", "rollingInterval": "Day", "retainedFileCountLimit": 7, "fileSizeLimitBytes": null, "buffered": true } } ] } } }, { //JSON形式で、Warning以上の構造化ログを出力するためのLogger "Name": "Logger", "Args": { "configureLogger": { "Filter": [ { "Name": "ByIncludingOnly", "Args": { "expression": "(@Level = 'Error' or @Level = 'Fatal' or @Level = 'Warning')" } } ], "WriteTo": [ { "Name": "File", "Args": { "path": "Logs/Error/Compact/error_compact.json", "formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact", "rollingInterval": "Day", "retainedFileCountLimit": 7, "fileSizeLimitBytes": null, "buffered": true } } ] } } } ] } }
- 投稿日:2021-03-05T12:09:07+09:00
CSVHelper ver 25.0 -> 26.1 チェンジログ
CSVHelper の情報共有です。
CSVHelper 26.1 が出ました。26.0 が主なリリース版で、26.1 で軽微な修正が行われています。
修正点は次の通りです。
- config クラスで ExceptionMessagesContainRawData がデフォルトで true になりました。
- 全てのクラスで init プロパティが削除されました。
- ヘッダレコードに含まれる例外メッセージが改善されました。
また、前バージョンとの互換性に影響がでる変更があります。
- IParserConfiguration に bool ExceptionMessagesContainRawData { get; } が追加されました。
- IWriterConfiguration に bool ExceptionMessagesContainRawData { get; } が追加されました。
- 全てのデリゲートオブジェクトから init メソッドが削除され、パラメータを指定できるコンストラクタが追加されました。対象は次の通りです。
- BadDataFound
- ConvertFromString
- GetConstructor
- GetDynamicPropertyName
- HeaderValidated
- MissingFieldFound
- PrepareHeaderForMatch
- ReadingExceptionOccurred
- ReferenceHeaderPrefix
- ShouldQuote
- ShouldSkipRecord
- ShouldUseConstructorParameter
- Validate
コンストラクタでパラメータ指定してインスタンス化出来る方が、コードが直感的になっていいと思います。
開発者の方々に感謝です。なお、オリジナルはこちらから参照できます。
https://joshclose.github.io/CsvHelper/change-log/26.0.1
Bug Fixes
- Fixed issue with constant not working when the field is missing.
26.0.0
Features
- Added configuration for ExceptionMessagesContainRawData that defaults to true.
Bug Fixes
- Removed all init properties. These were causing people too many problems.
- Fixed issue with exception message not containing the header record.
Breaking Changes
- Added bool IParserConfiguration.ExceptionMessagesContainRawData { get; }.
- Added bool IWriterConfiguration.ExceptionMessagesContainRawData { get; }.
- All delegate args objects have init removed and now have constructors with parameters.
- BadDataFound
- ConvertFromString
- GetConstructor
- GetDynamicPropertyName
- HeaderValidated
- MissingFieldFound
- PrepareHeaderForMatch
- ReadingExceptionOccurred
- ReferenceHeaderPrefix
- ShouldQuote
- ShouldSkipRecord
- ShouldUseConstructorParameter
- Validate
- 投稿日:2021-03-05T04:16:41+09:00
Unityエディタ拡張 ビルド前処理と後処理の今時の書き方
はじめに
- エディタ拡張で、ビルド前に動的にコードを生成したくて調べたら、インターフェイスを継承してコールバックを使うという記事がいくつか見つかりました。
- 前処理は
IPreprocessBuild.OnPreprocessBuild
で、後処理はIPostprocessBuild.OnPostprocessBuild
が紹介されています。- ところが、書いてみたら
obsolete
で警告されました。- そこから色々調べた結果です。
環境
- Unity 2018.4.x, 2019.4.x
今時の方法
リンク先の「公式スクリプトリファレンス」にもコード例があります。
以降の例は、ほぼ公式のままなので、リンク先をご覧いただいた方が分かり易いかも知れません。ビルド前処理
- ビルド開始前に呼ばれます。
- インターフェイスを継承したクラスを用意して、コールバックを受けます。
IPreprocessBuildWithReport.OnPreprocessBuild
コールバック (公式スクリプトリファレンス)例
- 方針
- 別クラスで定義されている変数を表示するだけのシーンを用意します。
- エディタ拡張で、ビルド直前に、クラスが定義されたファイルを動的に生成します。
- 生成されたコードによって、変数は現在日時で初期化されます。
- 生成されているファイルの初期化子と、実行で表示された日時が一致すれば成功です。
- このやり方で、実行時には取得できない(あるいは取得の面倒な)
PlayerSettings
の要素などをコードに取り込むことが可能です。Assets/Editor/PreBuild.csusing System; using System.IO; using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; using UnityEngine; public class PreBuild : IPreprocessBuildWithReport { public int callbackOrder { get { return 0; } } // ビルド前処理の中での処理優先順位 (0で最高) public void OnPreprocessBuild (BuildReport report) { Debug.Log ($"IPreprocessBuildWithReport.OnPreprocessBuild for {report.summary.platform} at {report.summary.outputPath}"); var ScriptPathPrefKey = $"{PlayerSettings.companyName}/{PlayerSettings.productName}/ScriptPath"; var AssetPath = EditorPrefs.GetString (ScriptPathPrefKey, "Assets/"); File.WriteAllText (Path.Combine (AssetPath, "Data.cs"), // 要するに、Assets/Data.csに以下を書き出す $@"public class Data {{ public static readonly string BuildDateTime = ""{DateTime.Now}""; // コード生成時の日時 }}"); AssetDatabase.Refresh (); // アセットDBの更新 } }ビルド中処理
- シーンがビルドされる毎に呼ばれるようです。
- インターフェイスを継承したクラスを用意して、コールバックを受けます。
IProcessSceneWithReport.OnProcessScene
コールバック (公式スクリプトリファレンス)例
Assets/Editor/SceneBuilded.csusing UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; using UnityEngine; public class SceneBuilded : IProcessSceneWithReport { public int callbackOrder { get { return 0; } } public void OnProcessScene (UnityEngine.SceneManagement.Scene scene, BuildReport report) { Debug.Log ($"IProcessSceneWithReport.OnProcessScene {scene.name} as {report.name}"); } }ビルド後処理
- メソッドにアトリビュートを付けることで、ビルド完了後に呼ばれるようです。
PostProcessBuildAttribute
アトリビュート (公式スクリプトリファレンス)例
Assets/Editor/PostBuild.csusing UnityEditor; using UnityEditor.Callbacks; using UnityEngine; public class PostBuild { [PostProcessBuild (0)] public static void OnPostprocessBuild (BuildTarget target, string pathToBuiltProject) { Debug.Log ($"OnPostprocessBuild for {target} at {pathToBuiltProject}"); } }ビルド結果
ConsoleIPreprocessBuildWithReport.OnPreprocessBuild for StandaloneWindows at D:/development/PreBuildTest/PreBuildTest.win/PreBuildTest.exe IProcessSceneWithReport.OnProcessScene SampleScene as New Report OnPostprocessBuild for StandaloneWindows at D:/development/PreBuildTest/PreBuildTest.win/PreBuildTest.exe Build completed with a result of 'Succeeded' in 12 seconds (12068 ms)
- 投稿日:2021-03-05T00:14:14+09:00
C# / PowerShell で JSON に突っ込める DateTime 文字列を出力する
まさかの一発オプション… 知らなかった… (´・ω・`)
C#:
DateTime.Now.ToString("o");PowerShell:
PS > Get-Date -Format o出力:
2021-03-04T23:33:42.6791453+09:00JSON と DateTime
JSON には DateTime 型がないので、DateTime 値を文字列に変換する必要がある。ここで考慮しなければならないのが、「文字列で表現した DateTime 値をちゃんと逆変換できるのか?」という点。特に、 サードパーティ製サービスとの通信を JSON で実施する場合、そのサービスがちゃんと逆変換してくれる形式である必要がある (もちろんサービスがフォーマット指定してるならそれに習うべき)。
一般論としてどんな形式がよく使われているか?と考えてみると、 ISO 8601 (ex:2021-03-04T23:33:42.6791453+09:00
) が1つの候補として挙げられると思う。C# で DateTime 型をこの形式の文字列に出力する場合、これまで自分はフォーマットを指定していたのだが…
DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffffffK");色々と調べた結果、前述のように一発オプションで出力できることが判明…
背景
GCP の BigQuery でテーブルを作成する際、サンプルデータ (JSONL) を使ってスキーマ自動生成をしようとした。DateTime 型データを JSON でどのような形式にすれば BigQuery が DateTime 型として認識してくれるか?を色々と思考錯誤した結果、 ISO 8601 形式に落ち着いた。
参考資料