20210305のC#に関する記事は5件です。

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

とくにWPFUWPの違いが分からない。

  • UWPC++/WinRTC++/CX
  • WPFC++/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 とは

Xamarin は、.NET を使用して、iOS、Android、Windows 向けの最新で高性能なアプリケーションをビルドするためのオープンソースのプラットフォームです。

What is the Uno Platform?

Unoプラットフォームは、UWPベースのコード(C#およびXAML)をiOS、Android、およびWebAssemblyで実行できるようにするユニバーサルWindowsプラットフォームブリッジです。

.NET MAUI is:

マルチプラットフォームのネイティブUI
モバイルとデスクトップ全体の複数のデバイスにデプロイ
単一のプロジェクト、単一のコードベースを使用
Xamarin.Forms進化
.NET 6

つまり・・・
Xamarin.FormsMAUIに進化した。
WebAssemblyに対応しているのはUno Platformだけ。

WPFの参考記事を多数執筆されているokazukiさんによるUno Platformデモ
C# で湯婆婆を Windows アプリと WebAssembly 対応してみた(無駄に MVVM)

脱線してC++

Qtがデファクトスタンダードなんですかね。
できることが多すぎる・・・

What is Qt?

Qtは、デスクトップ、組み込み、モバイル向けのクロスプラットフォームのアプリケーション開発フレームワークです。サポートされているプラ​​ットフォームには、Linux、OS X、Windows、VxWorks、QNX、Android、iOS、BlackBerry、SailfishOSなどがあります。

WebAssemblyにも対応
Qtを使って、デスクトップアプリ開発とWebAssemblyアプリ開発を同時に学ぼう

qt-wiki00.png

さらに脱線してPython

Qt + Python = デスクトップアプリケーションを作ることもできる

qt-creator.png

結論

MAUIを使います。
Visual Studio 2019での使い方を調べる

以上

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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
                }
              }
            ]
          }
        }
      }
    ]
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CSVHelper ver 25.0 -> 26.1 チェンジログ

CSVHelper の情報共有です。

CSVHelper 26.1 が出ました。26.0 が主なリリース版で、26.1 で軽微な修正が行われています。
修正点は次の通りです。

  • config クラスで ExceptionMessagesContainRawData がデフォルトで true になりました。
  • 全てのクラスで init プロパティが削除されました。
  • ヘッダレコードに含まれる例外メッセージが改善されました。

また、前バージョンとの互換性に影響がでる変更があります。

  1. IParserConfiguration に bool ExceptionMessagesContainRawData { get; } が追加されました。
  2. IWriterConfiguration に bool ExceptionMessagesContainRawData { get; } が追加されました。
  3. 全てのデリゲートオブジェクトから init メソッドが削除され、パラメータを指定できるコンストラクタが追加されました。対象は次の通りです。
    1. BadDataFound
    2. ConvertFromString
    3. GetConstructor
    4. GetDynamicPropertyName
    5. HeaderValidated
    6. MissingFieldFound
    7. PrepareHeaderForMatch
    8. ReadingExceptionOccurred
    9. ReferenceHeaderPrefix
    10. ShouldQuote
    11. ShouldSkipRecord
    12. ShouldUseConstructorParameter
    13. 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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unityエディタ拡張 ビルド前処理と後処理の今時の書き方

はじめに

  • エディタ拡張で、ビルド前に動的にコードを生成したくて調べたら、インターフェイスを継承してコールバックを使うという記事がいくつか見つかりました。
  • そこから色々調べた結果です。

環境

  • Unity 2018.4.x, 2019.4.x

今時の方法

リンク先の「公式スクリプトリファレンス」にもコード例があります。
以降の例は、ほぼ公式のままなので、リンク先をご覧いただいた方が分かり易いかも知れません。

ビルド前処理

  • 方針
    • 別クラスで定義されている変数を表示するだけのシーンを用意します。
    • エディタ拡張で、ビルド直前に、クラスが定義されたファイルを動的に生成します。
    • 生成されたコードによって、変数は現在日時で初期化されます。
    • 生成されているファイルの初期化子と、実行で表示された日時が一致すれば成功です。
  • このやり方で、実行時には取得できない(あるいは取得の面倒な)PlayerSettingsの要素などをコードに取り込むことが可能です。
Assets/Editor/PreBuild.cs
using 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の更新
    }

}

ビルド中処理

Assets/Editor/SceneBuilded.cs
using 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}");
    }

}

ビルド後処理

Assets/Editor/PostBuild.cs
using 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}");
    }

}

ビルド結果

Console
IPreprocessBuildWithReport.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)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# / PowerShell で JSON に突っ込める DateTime 文字列を出力する

まさかの一発オプション… 知らなかった… (´・ω・`)

C#:

DateTime.Now.ToString("o");

PowerShell:

PS > Get-Date -Format o

出力:

2021-03-04T23:33:42.6791453+09:00

JSON と 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 形式に落ち着いた。

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む