⚛️
スレッドセーフを保証するクラス
Swift の actor[1]、defer[2] を C# で再現してみました。
Atom<T>
C# 版の actor は DSL っぽい構文です。コレは Swift っぽくしたかったというよりは、スレッドセーフを強制する為に仕方なくという感じ。
var atom = new Atom<int>();
atom.Set = 42;
atom.Update = v => ++v; // 43
atom.Set = 310;
atom.Read = v => Console.WriteLine(v);
読み取りのスレッドセーフをシンプルな構文で強制するのが難しい。
👇 .NET 7+ なら関数型言語っぽい書き方も出来る。
atom <<= 310;
atom >>= Console.WriteLine;
コイツらとは別に C# の標準的なスタイルで書けるバージョンもある。コッチは主にクロージャのアロケーション回避が目的。
atom.WriteLock((x: 42, y: "Tuple"), static (args, current) =>
{
return current + args.x + args.y.Length;
});
参照型は ReadLock スコープ内で編集できちゃう。しょうがないね。
var atom = new Atom<MyClass>(value: new());
atom.ReadLock(foo, async static (foo, myClass) =>
{
// 👇 ブロック全体がロック内なので1秒間他のスレッドはアクセス不能になる
await Task.Delay(1000);
myClass.Data = foo.Value;
});
// 👇 アロケを避けつつロック時間を最小化(適切ではない場合もある)
atom.ReadLock(foo, (foo, value) => foo.Data = value);
async foo.ProcessDataAsync();
ソースコード
defer
C# 実装が既にある。言語レベルでのサポートじゃないので正直微妙。Go にもある機能だし書けない C# がおかしいレベル。
// ブロックを抜ける時にストリームを巻き戻す
using var _ = stream.Defer(stream => stream.Position = 0);
ソースコード
guard
C# に一番欲しくて一番難しい奴。最近 ?? throw の代わりにヘルパーを使う方法が分かったけど return はどう逆立ちしても不可能。
[DoesNotReturn] static T Throw<T>() => throw new Exception();
int? value = null;
var x = value ?? Throw<int>();
おわりに
⬆⬆⬆ https://github.com/dotnet/csharplang/discussions/9695 ⬆⬆⬆
以上です。お疲れ様でした。
Discussion