DateOnly and TimeOnly
. NET 6 introduces two long-awaited types - DateOnly and TimeOnly. They represent the date or time portion of DateTime.
// public DateOnly(int year, int month, int day) // public DateOnly(int year, int month, int day, Calendar calendar) DateOnly dateOnly = new(2021, 9, 25); Console.WriteLine(dateOnly); // Output: 25-Sep-21 // public TimeOnly(int hour, int minute) // public TimeOnly(int hour, int minute, int second) // public TimeOnly(int hour, int minute, int second, int millisecond) // public TimeOnly(long ticks) TimeOnly timeOnly = new(19, 0, 0); Console.WriteLine(timeOnly); // Output: 19:00 PM DateOnly dateOnlyFromDate = DateOnly.FromDateTime(DateTime.Now); Console.WriteLine(dateOnlyFromDate); // Output: 23-Sep-21 TimeOnly timeOnlyFromDate = TimeOnly.FromDateTime(DateTime.Now); Console.WriteLine(timeOnlyFromDate); // Output: 21:03 PM
Parallel.Foreachasync
It allows you to control the parallelism of scheduled asynchronous work.
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; } } // Output: // 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
ArgumentNullException.ThrowIfNull()
ArgumentNullException is a good small improvement. There is no need to check for null in each method before throwing an exception. Now it's a line.
ExampleMethod(null); void ExampleMethod(object param) { ArgumentNullException.ThrowIfNull(param); // Do something }
PriorityQueue
Yes NET 6. PriorityQueue represents the lowest priority queue. Each element is queued with an associated priority that determines the de queueing order. The lowest numbered element is dequeued first.
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); } // Output: // First // Second // Third 2 // Third 1 // Fourth
Read and write files
. NET 6 introduces a new low-level API for reading and writing files without FileStream. A new type of RandomAccess provides an offset based API for reading and writing files in a thread safe manner.
using SafeFileHandle handle = File.OpenHandle("file.txt", access: FileAccess.ReadWrite); // Write to file byte[] strBytes = Encoding.UTF8.GetBytes("Hello world"); ReadOnlyMemory<byte> buffer1 = new(strBytes); await RandomAccess.WriteAsync(handle, buffer1, 0); // Get file length long length = RandomAccess.GetLength(handle); // Read from file 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
New cycle timer
Satisfy a fully asynchronous "periodic timer". It allows asynchronous waiting for the timer to tick. It has a method, 'WaitForNextTickAsync', which waits for the next tick of the timer or the timer stops.
// One constructor: public PeriodicTimer(TimeSpan period) using PeriodicTimer timer = new(TimeSpan.FromSeconds(1)); while (await timer.WaitForNextTickAsync()) { Console.WriteLine(DateTime.UtcNow); } // Output: // 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 // ...
Index interface
. NET 6 implements the OpenTelemetry Metrics API specification. The instrument class creates an instrument object. There are instruments:
- Counter
- histogram
- Observable counter
- Observable measuring tool
You can even listen to the meter.
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); // Create Meter var meter = new Meter("MetricsApp", "v1.0"); // Create counter Counter<int> counter = meter.CreateCounter<int>("Requests"); app.Use((context, next) => { // Record the value of measurement counter.Add(1); return next(context); }); app.MapGet("/", () => "Hello World"); StartMeterListener(); app.Run(); // Create and start Meter Listener void StartMeterListener() { var listener = new MeterListener(); listener.InstrumentPublished = (instrument, meterListener) => { if (instrument.Name == "Requests" && instrument.Meter.Name == "MetricsApp") { // Start listening to a specific measurement recording meterListener.EnableMeasurementEvents(instrument, null); } }; listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) => { Console.WriteLine($"Instrument {instrument.Name} has recorded the measurement: {measurement}"); }); listener.Start(); }
Reflection API for nullability information
It provides nullable information and context from reflection members:
- parameter information
- Field information
- Attribute information
- Event information
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}"); } // Output: // Name property is Nullable // Value property is NotNull class Example { public string? Name { get; set; } public string Value { get; set; } }
Reflection API for nesting nullability information
It allows you to get nested nullability information. You can specify that the array property must be non null, but the element can be null, and vice versa. You can get nullability information of array elements.
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; } }
Process path and ID
You can access the process path and ID without assigning a new process instance.
int processId = Environment.ProcessId string path = Environment.ProcessPath; Console.WriteLine(processId); Console.WriteLine(path);
New configuration helper
Yes NET 6 added a new configuration helper "GetRequiredSection". An exception is thrown if the required configuration section is missing.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); WebApplication app = builder.Build(); MySettings mySettings = new(); // Throws InvalidOperationException if a required section of configuration is missing app.Configuration.GetRequiredSection("MySettings").Bind(mySettings); app.Run(); class MySettings { public string? SettingValue { get; set; } }
photoelectricity
You can easily generate a sequence of random values from an encrypted secure pseudo-random number generator (CSPNG).
It applies to encryption applications that:
- Key generation
- Nuns
- Salt in some signature schemes
// Fills an array of 300 bytes with a cryptographically strong random sequence of values. // GetBytes(byte[] data); // GetBytes(byte[] data, int offset, int count) // GetBytes(int count) // GetBytes(Span<byte> data) byte[] bytes = RandomNumberGenerator.GetBytes(300);
Native memory interface
. NET 6 introduces a new API to allocate native memory. The new NativeMemory type has methods for allocating and freeing memory.
unsafe { byte* buffer = (byte*)NativeMemory.Alloc(100); NativeMemory.Free(buffer); /* This class contains methods that are mainly used to manage native memory. 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); }*/ }
Power of 2
. NET 6 introduces a new helper to use the power of 2.
- IsPow2 evaluates whether the specified value is a power of 2.
- RoundUpToPowerOf2 rounds the specified value to a power of 2.
// IsPow2 evaluates whether the specified Int32 value is a power of two. Console.WriteLine(BitOperations.IsPow2(128)); // True // RoundUpToPowerOf2 rounds the specified T:System.UInt32 value up to a power of two. Console.WriteLine(BitOperations.RoundUpToPowerOf2(200)); // 256
Asynchronous on wait task
You can more easily wait for tasks to finish executing asynchronously. When the operation timeout expires, a timeout exception is thrown.
⚠️ This is an operation that cannot be cancelled!
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."); } // Output: // DoSomethingLongAsync started. // Unhandled exception.System.TimeoutException: The operation has timed out.
New math API
new method:
- New Coase
- Reciprocal estimation
- ReciprocalSqrtEstimate
New overload:
- Min, Max, ABS, symbol, clamp support, matte and asymptomatic
- DivRem variant returns tuples
// New methods SinCos, ReciprocalEstimate and ReciprocalSqrtEstimate // Simultaneously computes Sin and Cos (double sin, double cos) = Math.SinCos(1.57); Console.WriteLine($"Sin = {sin}\nCos = {cos}"); // Computes an approximate of 1 / x double recEst = Math.ReciprocalEstimate(5); Console.WriteLine($"Reciprocal estimate = {recEst}"); // Computes an approximate of 1 / Sqrt(x) double recSqrtEst = Math.ReciprocalSqrtEstimate(5); Console.WriteLine($"Reciprocal sqrt estimate = {recSqrtEst}"); // New overloads // Min, Max, Abs, Clamp and Sign supports nint and 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 variants return a tuple (int quotient, int remainder) = Math.DivRem(2, 7); Console.WriteLine($"Quotient = {quotient}\nRemainder = {remainder}"); // Output: // 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
CollectionsMarshal.GetValueRefOrNullRef
It returns a reference to a structure value that can be updated in place. It is not used for general purposes, but for high-performance solutions.
Dictionary<int, MyStruct> dictionary = new() { { 1, new MyStruct { Count = 100 } } }; int key = 1; ref MyStruct value = ref CollectionsMarshal.GetValueRefOrNullRef(dictionary, key); // Returns Unsafe.NullRef<TValue>() if it doesn't exist; check using Unsafe.IsNullRef(ref value) if (!Unsafe.IsNullRef(ref value)) { Console.WriteLine(value.Count); // Output: 100 // Mutate in-place value.Count++; Console.WriteLine(value.Count); // Output: 101 } struct MyStruct { public int Count { get; set; } }
Configure host options
New configuration host option API on IHostBuilder. It makes application setup easier.
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); }); }
Create asynchronous scope
. NET 6 introduces a new CreateAsyncScope method for creating AsyncServiceScope. The existing CreateScope method throws an exception when releasing the IAsyncDisposable service. CreateAsyncScope provides a simple solution.
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; }
Simplified encryption operation call mode
New method on symmetricalalgorithm to avoid streams if the payload is already in memory:
- decrypt
- decrypt
- decrypt
- Encrypted Cbc
- EncryptCfb
- EncryptEcb
They provide a simple way to use the encryption API.
static byte[] Decrypt(byte[] key, byte[] iv, byte[] ciphertext) { using (Aes aes = Aes.Create()) { aes.Key = key; return aes.DecryptCbc(ciphertext, iv, PaddingMode.PKCS7); } }