
1. DateOnly & TimeOnly
.NET 6 では、待望の2つの型 DateOnly と TimeOnly が導入されました。これらはそれぞれ、DateTime の日付部分と時刻部分を表します。
DateOnly dateOnly = new(2021, 9, 25);
Console.WriteLine(dateOnly);
TimeOnly timeOnly = new(19, 0, 0);
Console.WriteLine(timeOnly);
DateOnly dateOnlyFromDate = DateOnly.FromDateTime(DateTime.Now);
Console.WriteLine(dateOnlyFromDate);
TimeOnly timeOnlyFromDate = TimeOnly.FromDateTime(DateTime.Now);
Console.WriteLine(timeOnlyFromDate);
2. Parallel.ForEachAsync
複数の非同期タスクの並列度を制御できます。
var userHandlers = new[]
{
"users/okyrylchuk",
"users/jaredpar",
"users/davidfowl"
};
using HttpClient client = new()
{
BaseAddress = new Uri("https://api.github.com"),
};
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("DotNet", "6"));
ParallelOptions options = new()
{
MaxDegreeOfParallelism = 3
};
await Parallel.ForEachAsync(userHandlers, options, async (uri, token) =>
{
var user = await client.GetFromJsonAsync<GitHubUser>(uri, token);
Console.WriteLine($"Name: {user.Name}\nBio: {user.Bio}\n");
});
public class GitHubUser
{
public string Name { get; set; }
public string Bio { get; set; }
}
// 出力:
// Name: David Fowler
// Bio: Partner Software Architect at Microsoft on the ASP.NET team, Creator of SignalR
//
// Name: Oleg Kyrylchuk
// Bio: Software developer | Dotnet | C# | Azure
//
// Name: Jared Parsons
// Bio: Developer on the C# compiler
3. ArgumentNullException.ThrowIfNull()
ArgumentNullException の小さな改善です。例外をスローする前に各メソッド内で null チェックを行う必要がなくなり、response.EnsureSuccessStatusCode(); と同様に1行で記述できるようになりました。
ExampleMethod(null);
void ExampleMethod(object param)
{
ArgumentNullException.ThrowIfNull(param);
// 何か処理を行う
}
4. PriorityQueue
.NET 6 で新しく追加されたデータ構造 PriorityQueue です。キュー内の各要素には関連する優先度があり、これによってデキュー順序が決まります。番号が小さい要素ほど優先的にデキューされます。
PriorityQueue<string, int> priorityQueue = new();
priorityQueue.Enqueue("Second", 2);
priorityQueue.Enqueue("Fourth", 4);
priorityQueue.Enqueue("Third 1", 3);
priorityQueue.Enqueue("Third 2", 3);
priorityQueue.Enqueue("First", 1);
while (priorityQueue.Count > 0)
{
string item = priorityQueue.Dequeue();
Console.WriteLine(item);
}
// 出力:
// First
// Second
// Third 2
// Third 1
// Fourth
5. RandomAccess
スレッドセーフな方法でファイルの読み書きを行うためのオフセットベースの API を提供します。
using SafeFileHandle handle = File.OpenHandle("file.txt", access: FileAccess.ReadWrite);
// ファイルに書き込む
byte[] strBytes = Encoding.UTF8.GetBytes("Hello world");
ReadOnlyMemory<byte> buffer1 = new(strBytes);
await RandomAccess.WriteAsync(handle, buffer1, 0);
// ファイル長を取得
long length = RandomAccess.GetLength(handle);
// ファイルから読み取る
Memory<byte> buffer2 = new(new byte[length]);
await RandomAccess.ReadAsync(handle, buffer2, 0);
string content = Encoding.UTF8.GetString(buffer2.ToArray());
Console.WriteLine(content); // Hello world
6. PeriodicTimer
完全に非同期な PeriodicTimer が登場しました。非同期シナリオでの使用に適しており、WaitForNextTickAsync メソッドを持っています。
// コンストラクタは1つ: public PeriodicTimer(TimeSpan period)
using PeriodicTimer timer = new(TimeSpan.FromSeconds(1));
while (await timer.WaitForNextTickAsync())
{
Console.WriteLine(DateTime.UtcNow);
}
// 出力:
// 13 - Oct - 21 19:58:05 PM
// 13 - Oct - 21 19:58:06 PM
// 13 - Oct - 21 19:58:07 PM
// 13 - Oct - 21 19:58:08 PM
// 13 - Oct - 21 19:58:09 PM
// 13 - Oct - 21 19:58:10 PM
// 13 - Oct - 21 19:58:11 PM
// 13 - Oct - 21 19:58:12 PM
// ...
7. Metrics API
.NET 6 は OpenTelemetry Metrics API 仕様を実装し、組み込みのメトリクス API を提供します。Meter クラスを使用して以下のメトリクスを作成できます。
- Counter
- Histogram
- ObservableCounter
- ObservableGauge
使用方法は次のとおりです。
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Meter を作成
var meter = new Meter("MetricsApp", "v1.0");
// Counter を作成
Counter<int> counter = meter.CreateCounter<int>("Requests");
app.Use((context, next) =>
{
// 計測値を記録
counter.Add(1);
return next(context);
});
app.MapGet("/", () => "Hello World");
StartMeterListener();
app.Run();
// Meter Listener を作成して開始
void StartMeterListener()
{
var listener = new MeterListener();
listener.InstrumentPublished = (instrument, meterListener) =>
{
if (instrument.Name == "Requests" && instrument.Meter.Name == "MetricsApp")
{
// 特定の計測記録のリッスンを開始
meterListener.EnableMeasurementEvents(instrument, null);
}
};
listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) =>
{
Console.WriteLine($"Instrument {instrument.Name} has recorded the measurement: {measurement}");
});
listener.Start();
}
8. 要素が null 許容かどうかを確認するリフレクション API
リフレクションメンバーからの null 許容情報とコンテキストを提供します。
- ParameterInfo パラメータ
- FieldInfo フィールド
- PropertyInfo プロパティ
- EventInfo イベント
var example = new Example();
var nullabilityInfoContext = new NullabilityInfoContext();
foreach (var propertyInfo in example.GetType().GetProperties())
{
var nullabilityInfo = nullabilityInfoContext.Create(propertyInfo);
Console.WriteLine($"{propertyInfo.Name} property is {nullabilityInfo.WriteState}");
}
// 出力:
// Name property is Nullable
// Value property is NotNull
class Example
{
public string? Name { get; set; }
public string Value { get; set; }
}
9. ネストされた要素が null 許容かどうかを確認するリフレクション API
ネストされた要素の null 許容情報を取得できます。配列プロパティ自体は非 null だが、要素は null 許容に指定できます。また、その逆も可能です。
Type exampleType = typeof(Example);
PropertyInfo notNullableArrayPI = exampleType.GetProperty(nameof(Example.NotNullableArray));
PropertyInfo nullableArrayPI = exampleType.GetProperty(nameof(Example.NullableArray));
NullabilityInfoContext nullabilityInfoContext = new();
NullabilityInfo notNullableArrayNI = nullabilityInfoContext.Create(notNullableArrayPI);
Console.WriteLine(notNullableArrayNI.ReadState); // NotNull
Console.WriteLine(notNullableArrayNI.ElementType.ReadState); // Nullable
NullabilityInfo nullableArrayNI = nullabilityInfoContext.Create(nullableArrayPI);
Console.WriteLine(nullableArrayNI.ReadState); // Nullable
Console.WriteLine(nullableArrayNI.ElementType.ReadState); // Nullable
class Example
{
public string?[] NotNullableArray { get; set; }
public string?[]? NullableArray { get; set; }
}
10. ProcessId & ProcessPath
Environment クラスを使用して、プロセス ID とパスを直接取得できます。
int processId = Environment.ProcessId
string path = Environment.ProcessPath;
Console.WriteLine(processId);
Console.WriteLine(path);
11. Configuration に GetRequiredSection() が追加
DI の GetRequiredService() と同じように、不足している場合は例外がスローされます。
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
WebApplication app = builder.Build();
MySettings mySettings = new();
// 必須の構成セクションが欠落している場合、InvalidOperationException をスロー
app.Configuration.GetRequiredSection("MySettings").Bind(mySettings);
app.Run();
class MySettings
{
public string? SettingValue { get; set; }
}
12. CSPNG(暗号論的擬似乱数生成器)
暗号論的擬似乱数生成器 (CSPNG) から簡単にランダムな値のシーケンスを生成できます。
これは以下のシナリオで役立ちます。
- 鍵生成
- ナンス(nonce)
- 一部の署名スキームにおけるソルト(salt)
// 300 バイトの配列を暗号学的に強いランダムな値で埋める。
// GetBytes(byte[] data);
// GetBytes(byte[] data, int offset, int count)
// GetBytes(int count)
// GetBytes(Span<byte> data)
byte[] bytes = RandomNumberGenerator.GetBytes(300);
13. Native Memory API
.NET 6 では、ネイティブメモリを割り当てるための新しい API NativeMemory が導入されました。メモリの割り当てと解放を行うメソッドがあります。
unsafe
{
byte* buffer = (byte*)NativeMemory.Alloc(100);
NativeMemory.Free(buffer);
/* このクラスには、主にネイティブメモリを管理するためのメソッドが含まれています。
public static class NativeMemory
{
public unsafe static void* AlignedAlloc(nuint byteCount, nuint alignment);
public unsafe static void AlignedFree(void* ptr);
public unsafe static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment);
public unsafe static void* Alloc(nuint byteCount);
public unsafe static void* Alloc(nuint elementCount, nuint elementSize);
public unsafe static void* AllocZeroed(nuint byteCount);
public unsafe static void* AllocZeroed(nuint elementCount, nuint elementSize);
public unsafe static void Free(void* ptr);
public unsafe static void* Realloc(void* ptr, nuint byteCount);
}*/
}
14. Power of 2
.NET 6 では、2 の累乗を扱うための新しいメソッドが導入されました。
IsPow2:指定された値が 2 の累乗かどうかを判定します。RoundUpToPowerOf2:指定された値を 2 の累乗に切り上げます。
// IsPow2 は、指定された Int32 値が 2 の累乗かどうかを評価します。
Console.WriteLine(BitOperations.IsPow2(128)); // True
// RoundUpToPowerOf2 は、指定された UInt32 値を 2 の累乗に切り上げます。
Console.WriteLine(BitOperations.RoundUpToPowerOf2(200)); // 256
15. WaitAsync on Task
非同期タスクの実行をより簡単に待機でき、タイムアウトが発生した場合は TimeoutException がスローされます。
Task operationTask = DoSomethingLongAsync();
await operationTask.WaitAsync(TimeSpan.FromSeconds(5));
async Task DoSomethingLongAsync()
{
Console.WriteLine("DoSomethingLongAsync started.");
await Task.Delay(TimeSpan.FromSeconds(10));
Console.WriteLine("DoSomethingLongAsync ended.");
}
// 出力:
// DoSomethingLongAsync started.
// Unhandled exception.System.TimeoutException: The operation has timed out.
16. 新しい数学 API
新しいメソッド:
- SinCos
- ReciprocalEstimate
- ReciprocalSqrtEstimate
新しいオーバーロード:
- Min, Max, Abs, Sign, Clamp が nint と nuint をサポート
- DivRem は商と余りを含むタプルを返します。
// 新しいメソッド SinCos, ReciprocalEstimate, ReciprocalSqrtEstimate
// Sin と Cos を同時に計算
(double sin, double cos) = Math.SinCos(1.57);
Console.WriteLine($"Sin = {sin}\nCos = {cos}");
// 1 / x の近似値を計算
double recEst = Math.ReciprocalEstimate(5);
Console.WriteLine($"Reciprocal estimate = {recEst}");
// 1 / Sqrt(x) の近似値を計算
double recSqrtEst = Math.ReciprocalSqrtEstimate(5);
Console.WriteLine($"Reciprocal sqrt estimate = {recSqrtEst}");
// 新しいオーバーロード
// Min, Max, Abs, Clamp, Sign が nint と nuint をサポート
(nint a, nint b) = (5, 10);
nint min = Math.Min(a, b);
nint max = Math.Max(a, b);
nint abs = Math.Abs(a);
nint clamp = Math.Clamp(abs, min, max);
nint sign = Math.Sign(a);
Console.WriteLine($"Min = {min}\nMax = {max}\nAbs = {abs}");
Console.WriteLine($"Clamp = {clamp}\nSign = {sign}");
// DivRem の派生版はタプルを返す
(int quotient, int remainder) = Math.DivRem(2, 7);
Console.WriteLine($"Quotient = {quotient}\nRemainder = {remainder}");
// 出力:
// Sin = 0.9999996829318346
// Cos = 0.0007963267107331026
// Reciprocal estimate = 0.2
// Reciprocal sqrt estimate = 0.4472135954999579
// Min = 5
// Max = 10
// Abs = 5
// Clamp = 5
// Sign = 1
// Quotient = 0
// Remainder = 2
17. CollectionsMarshal.GetValueRefOrNullRef
これは、辞書内でループしたり変更可能な構造体を変更する際に使用します。構造体のコピーを減らし、辞書の再ハッシュ計算を回避できます。やや難解なので、興味があればこちらを参照してください。
Dictionary<int, MyStruct> dictionary = new()
{
{ 1, new MyStruct { Count = 100 } }
};
int key = 1;
ref MyStruct value = ref CollectionsMarshal.GetValueRefOrNullRef(dictionary, key);
// 存在しない場合は Unsafe.NullRef<TValue>() を返します。Unsafe.IsNullRef(ref value) でチェック
if (!Unsafe.IsNullRef(ref value))
{
Console.WriteLine(value.Count); // 出力: 100
// その場で変更
value.Count++;
Console.WriteLine(value.Count); // 出力: 101
}
struct MyStruct
{
public int Count { get; set; }
}
18. ConfigureHostOptions
IHostBuilder に新しい ConfigureHostOptions API が追加され、アプリケーションの設定がより簡単になりました。
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureHostOptions(o =>
{
o.ShutdownTimeout = TimeSpan.FromMinutes(10);
});
}
19. Async Scope
.NET 6 では新しい CreateAsyncScope メソッドが導入されました。IAsyncDisposable のサービスを処理する際、既存の CreateScope メソッドでは例外が発生しますが、CreateAsyncScope を使用することで完全に解決できます。
await using var provider = new ServiceCollection()
.AddScoped<Example>()
.BuildServiceProvider();
await using (var scope = provider.CreateAsyncScope())
{
var example = scope.ServiceProvider.GetRequiredService<Example>();
}
class Example : IAsyncDisposable
{
public ValueTask DisposeAsync() => default;
}
20. 暗号クラスの簡略化
- DecryptCbc
- DecryptCfb
- DecryptEcb
- EncryptCbc
- EncryptCfb
- EncryptEcb
static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext)
{
using (Aes aes = Aes.Create())
{
aes.Key = key;
return aes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7);
}
}
以上です...
著者: Oleg Kyrylchuk