C# の雑多なメモ
xUnit の InlineData
いつも忘れるのでめも
[Theory]
[InlineData(1, 2.1, "aa")]
[InlineData(4, 100.13, "bb")]
public void HogeTest(int x, double y, string result) { ... }
短い文字列の UTF-8 バイト列を得る
/*
byte[] ToUtf8Bytes(string s)
{
Span<byte> span = stackalloc byte[s.Length * 4];
var length = Encoding.UTF8.GetBytes(s, span);
return span.Slice(0, length).ToArray();
}*/
// 普通に byte[] を返すオーバーロードあったわ
string s = "sss";
byte[] utf8Bytes = Encoding.UTF8.GetBytes(s);
record struct
C# 10 で来そう.
参照先 csproj の appsettings をコピーしたくないときの対処
問題
たとえば
-
Project A (unit test project など)
appsettings.json
-
Project B
appsettings.Production.json
のようなプロジェクトがあるとする.
Project A -> Project B へのプロジェクト参照があって,
Project A で appsettings.json
を, Project B で appsettings.Production.json
をそれぞれ出力ディレクトリにコピーする設定になっているとする.
この場合, Project A の出力ディレクトリにも Project B 由来の appsettings.Production.json がコピーされるが,Project A での config の読み込みに Host.CreateDefaultBuilder
などを使っている場合,デフォルトの environment が Production
に設定されるため, Project A の appsettings.json よりも Project B 由来の appsettings.Production.json が優先されてしまう.
ここで, Project B に変更を入れずに, Project A の出力ディレクトリに appsettings.Production.json を作りたくないとき,どうしたらよいか.
解1
Project A の appsettings.json を appsettings.Production.json に rename する
ただ,単体テストプロジェクトでは
- production とは?って感じ. appsettings.Production.json にしている意図も伝わらない
- 他の Project B 由来の appsettings.Development.json などは出力ディレクトリに残る
解2
Project A の AfterBuild ターゲットのあとに, Project B 由来の appsettings.xxx.json を削除する処理を入れる.
csproj に下記追加
<!-- 参照している Project B から appsettings.xxx.json がコピーされ,設定が上書きされるため削除 -->
<Target Name="DeleteAppSettingsFromReferencedProject" AfterTargets="AfterBuild">
<Delete Files="$(TargetDir)appsettings.Production.json" />
<Delete Files="$(TargetDir)appsettings.Development.json" />
</Target>
ビルド時に Project B 由来の appsettings.Production.json
がコピーされるが, AfterBuild
の処理により削除される.
CsvHelper の CultureInfo の扱いについて
本番環境と開発環境で CsvHelper の出力結果が変わってしまいハマったのでメモ。
古いバージョン (v12) をカルチャ指定なしで使っており、 CultureInfo.CurrentCulture
相当のものが使われていたもよう。
テスト結果は windows 10, .NET 5 (LINQ Pad) の日本語環境で実行したもの。
現行 ver (v27.0.4)
CsvWriter
のコンストラクタに CultureInfo
を渡す(必須。null
では NullReferenceException
)
CultureInfo
によって DateTime
など、カルチャ依存のものは結果が変わる。
テスト用コード:
using System.Globalization;
void Main()
{
var record = new Hoge(1, DateTime.Now);
// WriteCsvTest("null", record, null).Dump(); null reference exception
WriteCsvTest("invariant culture", record, CultureInfo.InvariantCulture).Dump();
WriteCsvTest("current culture", record, CultureInfo.CurrentCulture).Dump();
WriteCsvTest("ja-jp", record, CultureInfo.GetCultureInfo("ja-JP")).Dump();
}
// You can define other methods, fields, classes and namespaces here
public record Hoge(int Id, DateTime Date);
public record WritingResult(string Name, string result);
static WritingResult WriteCsvTest(string testCaseName, Hoge record, CultureInfo cultureInfo)
{
using var memory = new MemoryStream();
using (var writer = new StreamWriter(memory))
using (var csvWriter = new CsvWriter(writer, cultureInfo))
{
csvWriter.WriteRecord(record);
}
var result = Encoding.UTF8.GetString(memory.ToArray());
return new WritingResult(testCaseName, result);
}
結果:
旧 ver (v12.0)
旧バージョンでは CultureInfo
は必須でない。
指定がない場合は CultureInfo.CurrentCulture
と同じ動作をする。→ カルチャが異なる環境で実行した場合、シリアライズ結果が変わってしまうので注意。
テスト用コード:
void Main()
{
var record = new Hoge(1, DateTime.Now);
WriteCsvTest("null", record, null).Dump();
WriteCsvTest("invariant culture", record, CultureInfo.InvariantCulture).Dump();
WriteCsvTest("current culture", record, CultureInfo.CurrentCulture).Dump();
WriteCsvTest("ja-jp", record, CultureInfo.GetCultureInfo("ja-JP")).Dump();
}
// You can define other methods, fields, classes and namespaces here
public record Hoge(int Id, DateTime Date);
public record WritingResult(string Name, string result);
static WritingResult WriteCsvTest(string testCaseName, Hoge record, CultureInfo cultureInfo)
{
using var memory = new MemoryStream();
using (var writer = new StreamWriter(memory))
using (var csvWriter = new CsvWriter(writer, new Configuration() { CultureInfo = cultureInfo }, true))
{
csvWriter.WriteRecord(record);
}
var result = Encoding.UTF8.GetString(memory.ToArray());
return new WritingResult(testCaseName, result);
}
結果: