Show / Hide Table of Contents

Migration from 5.x

.NEXT 6.x requires .NET 10 SDK and C# 14 language features. C# 14 introduces extension members feature that changes the .NEXT API shape significantly. Now, the extensions can be expressed as extension members (properties and methods) for the existing classes.

Core library

The following obsolete members are removed:

  • ConcurrentCache, CacheEvictionPolicy. Replaced with RandomAccessCache<TKey, TValue> from DotNext.Threading library
  • DotNext.Buffers.Text.Hex static class. Use Convert class for fast HEX conversion
  • AsyncEnumerable helpers removed in favor of AsyncEnumerable class

User Data

GetUserData extension method is refactored to UserData extension property:

UserData data = obj.GetUserData();  // 5.x
UserData data = obj.UserData;       // 6.x

Array Extensions

IsNullOrEmpty extension method is converted to static extension method to provide a symmetry with string.IsNullOrEmpty static method:

int[] array;
bool isNull = array.IsNullOrEmpty();        // 5.x
bool isNull = Array.IsNullOrEmpty(array);   // 6.x

Optional and Result types

Optional.IsOptional and Result.IsResult extension methods are converted to extension properties:

bool isOpt = typeof(Optional<int>).IsOptional();    // 5.x
bool isOpt = typeof(Optional<int>).IsOptional;      // 6.x

Random Extensions

NextString is no longer availble in favor of GetString method. Also, extension methods for RandomNumberGenerator instance are converted to static extension methods:

int rnd = RandomNumberGenerator.Create().Next();    // 5.x
int rnd = RandomNumberGenerator.Next();             // 6.x

Delegate Helpers

Converter, Func, Predicate static classes are merge with DelegateHelpers static class. For instance, Func.Constant is a static extension method for Func<T> delegate type:

Func<int> f = Func.Constant<int>(42);   // 5.x
Func<int> f = Func<int>.Constant(42);   // 6.x

The same true for methods in Predicate static class:

Predicate<string> notNull = Predicate.IsNotNull<string>();  // 5.x
Predicate<string> notNull = Predicate<string>.IsNotNull;    // 6.x

DelegateHelpers.CreateDelegate overloads are renamed to FromPointer and marked as static extension methods for appropriate delegate types.

Func<int> d = DelegateHelpers.CreateDelegate<int>(methodPtr);   // 5.x
Func<int> d = Func<int>.FromPointer(methodPtr);                 // 6.x

Span static class

void Span.CopyTo<T>(this Span<T> source, Span<T> destination, out int writtenCount) is now available as the extension operator:

Span<byte> source, destination;
int writtenCount = source >> destination;

Epoch class

Epoch class is now sealed. There is just one Enter method that doesn't provide reclamation. If you need to force a reclamation, you need to call Reclaim method on the scope returned by Enter method:

var epoch = new Epoch();
Epoch.RecycleBin bin;
using (var scope = epoch.Enter())
{
    bin = scope.Reclaim(true);
}

bin.Clear();

Also, the scope returned by Enter is now async-friendly.

Intrinsics class

Members of this class are moved to DotNext.Runtime.CompilerServices.AdvancedHelpers static class, and marked as static extension methods for Unsafe and RuntimeHelpers classes.

int alignment = Intrinsics.AlignOf<int>();  // 5.x
int alignment = Unsafe.AlignOf<int>();      // 6.x
int alignment = int.Alignment;              // 6.x

IsExactTypeOf is converted into a static extension method within BasicExtensions class.

Reflection

List.Indexer<T> and Dictionary.Indexer<TKey, TValue> inner classes are moved to CollectionType and DictionaryType as static extension methods:

Func<IReadOnlyList<string>, int, string> indexer = List.Indexer<string>.ReadOnly;   // 5.x
Func<IReadOnlyList<string>, int, string> indexer = IReadOnlyList<string>.Indexer;   // 6.x

StreamSource class

Its factory methods are converted to static extension methods for Stream class:

Stream s = StreamSource.AsStream(new byte[] { 10, 20 });    // 5.x
Stream s = Stream.Create(new byte[] { 10, 20 });            // 6.x

Atomic.Boolean type

The type is no longer needed due to presence of necessary method in Interlocked class. FalseToTrue and TrueToFalse methods are exposed as static extension methods for Interlocked class.

bool value;
Interlocked.FalseToTrue(ref value);

ServiceProviderFactory class

To override the service from the underlying IServiceProvider instance, use Override extension method rather than Create factory method which is removed. FromTuple is converted to static extension method:

IServiceProvider provider = IServiceProvider.Create<(string, int)>(("Service", 42));

LockAcquisition

All the methods are moved into Lock class.

EnumConverter

EnumConverter is replaced with DotNext.Numerics.Enum<T> type.

Range Endpoints

Enclosed() and Disclosed() extension method are replaced with extension properties:

bool r = 42.IsBetween(40.Enclosed, 43.Disclosed);

MemoryAllocator delegate

MemoryAllocator<T> delegate has extension property Default that can be used to obtain the default allocator. AllocateExactly and AllocateAtLeast don't accept nullable allocator anymore. These methods expect non-nullable allocator. It's possible to use DefaultIfNull to fix the nullability issue:

static void M(MemoryAllocator<byte>? allocator)
{
    using MemoryOwner<byte> owner = allocator.DefaultIfNull.AllocateExactly(64);
    // or
    allocator ??= MemoryAllocator<byte>.Default;
    using MemoryOwner<byte> owner = allocator.AllocateExactly(64);
}

Threading

The following obsolete members are removed:

  • LinkedTokenSourceFactory static class and its extension methods. Use CancellationTokenMultiplexer instead.

AsyncLockAcquisition

All the methods are moved into AsyncLock class.

AsyncBridge class

AsyncBridge.WaitAnyAsync is moved to CancellationTokenMultiplexer which is now a single entry point to work with combined cancellation tokens

IndexPool value type

IndexPool is no longer a value type. It's converted to class and the limitation on the maximum number to be returned from the pool is removed. The capacity of IndexPool is customizable. If you're using it to organize custom object pool, consider BoundedObjectPool<T> class instead, which is more convenient. IndexPool remains available for special situations, when you need to know whether the object is really taken from the pool or created on-the-fly.

CancellationTokenMultiplexer class

CancellationTokenMultiplexer class is converted to value type. Also, it has new WaitAnyAsync method that utilizes internal pool of cancellation token sources.

IO

Encoding string interpolation helpers are moved to Core library, DotNext.Text namespace.

DuplexStream is no longer available, but can be construced by using PipeExtensions.AsStream extension method.

SequenceReader type

It's moved from DotNext.IO to DotNext.Buffers namespace. Also, it cannot be construced by using IAsyncBinaryReader.Create factory method. Instead, the constructor of SequenceReader must be used.

TextStreamExtensions class

Most of the factory methods exposed by TextStreamExtensions are converted to static extension methods:

ReadOnlySequence<char> sequence;
TextReader reader = sequence.AsTextReader();        // 5.x
TextReader reader = TextReader.Create(sequence);    // 6.x

IAsyncBinaryReader and IAsyncBinaryWriter

IAsyncBinaryReader.Create and IAsyncBinaryWriter.Create methods are transformed into static extension methods. To call Create overload with PipeWriter parameter type, it's required to import DotNext.IO.Pipelines namespace.

Metaprogramming

Const() extension method is renamed to Quoted extension property.

Raft

MemoryBasedStateMachine and DiskBasedStateMachine are removed in favor of high-performance and flexible WriteAheadLog class as announced here. WriteAheadLog is no longer marked as experimental. These WALs are not binary-compatible. Moreover, WriteAheadLog from 5.x is not compatible with its counterpart in 6.x.

Migration from MemoryBasedStateMachine

To do that, you need to use version 5.x and write a migration of the log entries manually from the class that derives from MemoryBasedStateMachine and WriteAheadLog which is marked as experimental. Both classes implement the same IPersistentState interface. There are the steps to do a migration:

  1. Implement IStateMachine to provide a storage for the snapshot. Or you can reuse SimpleStateMachine as its base implementation
  2. Iterate over all log entries in MemoryBasedStateMachine and append them to WriteAheadLog instance

Now you can migrate WriteAheadLog from 5.x to 6.x by following the steps in from the next chapter.

Migration from experimental WriteAheadLog

WriteAheadLog from 5.x and 6.x are almost identical except the timestamps associated with every log entry. In 6.x, for the performance reasons, the support of timestamps are removed. Therefore, these logs are binary icompatible. However, you can opt-in the compatibility with DotNext.IO.WriteAheadLog.IgnoreTimestamp feature switch. To do that, go to CSPROJ file and add the switch:

<ItemGroup>
  <RuntimeHostConfigurationOption
      Include="DotNext.IO.WriteAheadLog.IgnoreTimestamp"
      Value="false" />
</ItemGroup>

The feature switch forces to interpret the space on the disk related to timestamps correctly. Thus, you can use a log created by WriteAheadLog class in 5.x. For a new log, this option is not recommended. By default, the log doesn't store or parse timestamps.

  • Edit this page
☀
☾
In this article
Back to top
Supported by the .NET Foundation
☀
☾