This post is part of the series 'C# 8'. Be sure to check out the rest of the blog posts of the series!
Index and Range are 2 new types that support the new language features: hat and range operators.
The ^ operator indicates the element position from the end of a sequence. For a sequence of length length, ^n points to the element with offset length - n from the start of a sequence. For example, ^1 points to the last element of a sequence, and ^length points to the first element of a sequence.
The .. operator specifies the start and end of a range of indices as its operands. The left-hand operand is an inclusive start of a range. The right-hand operand is an exclusive end of a range. Either of the operands can be an index from the start or the end of a sequence, as the following example shows:
C#
var str = "abcde";
Assert.Equal('e', str[^1]); // str[str.Length - 1]
Assert.Equal("bcd", str[1..^1]); // str.Substring(1, str.Length - 2)
Assert.Equal("abcd", str[..^1]); // str.Substring(0, str.Length - 1)
Assert.Equal("bcde", str[1..]); // str.Substring(1)
byte[] array = { 1, 2, 3, 4, 5 };
Assert.Equal(4, array[^2]);
Assert.Equal(new byte[] { 2, 3, 4 }, array[1..^1]);
ReadOnlySpan<byte> span = array.AsSpan();
Assert.Equal((ReadOnlySpan<byte>)new byte[] { 2, 3, 4 }, span[1..^1]);
The proposal explains all the details of these new operators and how the compiler handles them. Of particular interest is what your classes must implement to support the hat operator or the range operator.
These operators work out of the box with .NET Core 3.0. This post covers how to use them in .NET Standard 2.0 and .NET Framework.
#Enable C# 8 in your project
To use C# 8 features, you need to enable them in your project. This may already be the case by default, but you can be explicit by editing the csproj and adding <LangVersion>8.0</LangVersion>:
csproj (MSBuild project file)
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8.0</LangVersion> <!-- 👈 Enable C# 8 features -->
</PropertyGroup>
</Project>
#Add the required types
To use Index and Range from .NET Standard 2.0 or .NET Framework, the following types and methods must be declared in your code:
C#
// Implementation at https://www.meziantou.net/assets/range.zip
namespace System
{
public readonly struct Index
{
// To use the "hat" operator (^), the following is required:
public Index(int value, bool fromEnd);
// To use the System.Index type as an argument in an array element access, the following member is required:
int GetOffset(int length);
}
// The .. syntax for System.Range will require the System.Range type, as well as one or more of the following members:
public readonly struct Range
{
public Range(System.Index start, System.Index end);
public static Range StartAt(System.Index start);
public static Range EndAt(System.Index end);
public static Range All { get; }
}
}
namespace System.Runtime.CompilerServices
{
public static class RuntimeHelpers
{
// For a value of type System.Range to be used in an array element access expression, the following member must be present:
public static T[] GetSubArray<T>(T[] array, System.Range range);
}
}
You can copy the implementation from this snippet: full code.
This code is mostly copied from the CoreFX repository with small changes needed to compile for .NET Standard 2.0. CoreFX's implementation uses Span and CacheStringBuilder, which improves the performance of the ToString methods, but is not very useful in most projects. These have been replaced with simpler, less performant alternatives.
Index and Range types do not need to be public as long as they are not part of the public API of your assembly. The types in this snippet are internal because exposing them publicly would force consumers to use them instead of the BCL versions, and having multiple libraries declare these types as public would cause conflicts.
And voilà! You can use the hat and range operators in your project 😃
Do you have a question or a suggestion about this post? Contact me!