博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# 使用 Index 和 Range 简化集合操作
阅读量:4035 次
发布时间:2019-05-24

本文共 6828 字,大约阅读时间需要 22 分钟。

C# 使用 Index 和 Range 简化集合操作

Intro

有的语言数组的索引值是支持负数的,表示从后向前索引,比如:arr[-1]

从 C# 8 开始,C# 支持了数组的反向 Index,和 Range 操作,反向 Index 类似于其他语言中的负索引值,但其实是由编译器帮我们做了一个转换,Range 使得我们对数组截取某一部分的操作会非常简单,下面来看一下如何使用吧

Sample

使用 ^ 可以从集合的最后开始索引元素,如果从数组的最后开始索引元素,最后一个元素应该是 1 而不是0如:arr[^1]

使用 .. 可以基于某个数组截取集合中的某一段创建一个新的数组,比如 var newArray = array[1..^1],再来看一下下面的示例吧

int[] someArray = new int[5] { 1, 2, 3, 4, 5 };int lastElement = someArray[^1]; // lastElement = 5lastElement.Dump();someArray[3..5].Dump();someArray[1..^1].Dump();someArray[1..].Dump();someArray[..^1].Dump();someArray[..2].Dump();

输出结果如下:

output

Index

那么它是如何实现的呢,索引值引入了一个新的数据结构 System.Index,当你使用 ^ 运算符的时候,实际转换成了 Index

Index:

public readonly struct Index : IEquatable
{    public Index(int value, bool fromEnd = false);    /// 
Create an Index pointing at first element.    public static Index Start => new Index(0);    /// 
Create an Index pointing at beyond last element.    public static Index End => new Index(~0);    //    // Summary:    //     Gets a value that indicates whether the index is from the start or the end.    //    // Returns:    //     true if the Index is from the end; otherwise, false.    public bool IsFromEnd { get; }    //    // Summary:    //     Gets the index value.    //    // Returns:    //     The index value.    public int Value { get; }    //    // Summary:    //     Creates an System.Index from the end of a collection at a specified index position.    //    // Parameters:    //   value:    //     The index value from the end of a collection.    //    // Returns:    //     The Index value.    public static Index FromEnd(int value);    //    // Summary:    //     Create an System.Index from the specified index at the start of a collection.    //    // Parameters:    //   value:    //     The index position from the start of a collection.    //    // Returns:    //     The Index value.    public static Index FromStart(int value);    //    // Summary:    //     Returns a value that indicates whether the current object is equal to another    //     System.Index object.    //    // Parameters:    //   other:    //     The object to compare with this instance.    //    // Returns:    //     true if the current Index object is equal to other; false otherwise.    public bool Equals(Index other);    //    // Summary:    //     Calculates the offset from the start of the collection using the given collection length.    //    // Parameters:    //   length:    //     The length of the collection that the Index will be used with. Must be a positive value.    //    // Returns:    //     The offset.    public int GetOffset(int length);    //    // Summary:    //     Converts integer number to an Index.    //    // Parameters:    //   value:    //     The integer to convert.    //    // Returns:    //     An Index representing the integer.    public static implicit operator Index(int value);}

如果想要自己自定义的集合支持 Index 这种从数组最后索引的特性,只需要加一个类型是 Index 的索引器就可以了,正向索引也是支持的,int 会自动隐式转换为 Index,除了显示的增加 Index 索引器之外,还可以隐式支持,实现一个 int Count {get;} 的属性(属性名叫 Length 也可以),在实现一个 int 类型的索引器就可以了

写一个简单的小示例:

private class TestCollection{    public IList
 Data { get; init; }    public int Count => Data.Count;    public int this[int index] => Data[index];    //public int this[Index index] => Data[index.GetOffset(Data.Count)];}var array = new TestCollection(){    Data = new[] { 1, 2, 3 }};Console.WriteLine(array[^1]);Console.WriteLine(array[1]);

Range

Range 是在 Index 的基础上实现的,Range 需要两个 Index 来指定开始和结束

public readonly struct Range : IEquatable
{    /// 
Represent the inclusive start index of the Range.    public Index Start { get; }    /// 
Represent the exclusive end index of the Range.    public Index End { get; }    /// 
Construct a Range object using the start and end indexes.    /// 
Represent the inclusive start index of the range.    /// 
Represent the exclusive end index of the range.    public Range(Index start, Index end)    {        Start = start;        End = end;    }    /// 
Create a Range object starting from start index to the end of the collection.    public static Range StartAt(Index start) => new Range(start, Index.End);    /// 
Create a Range object starting from first element in the collection to the end Index.    public static Range EndAt(Index end) => new Range(Index.Start, end);    /// 
Create a Range object starting from first element to the end.    public static Range All => new Range(Index.Start, Index.End);    /// 
Calculate the start offset and length of range object using a collection length.    /// 
The length of the collection that the range will be used with. length has to be a positive value.    /// 
    /// For performance reason, we don't validate the input length parameter against negative values.    /// It is expected Range will be used with collections which always have non negative length/count.    /// We validate the range is inside the length scope though.    /// 
    public (int Offset, int Length) GetOffsetAndLength(int length);}

如何在自己的类中支持 Range 呢?

一种方式是自己直接实现一个类型是 Range 的索引器

另外一种方式是隐式实现,在自定义类中添加一个 Count 属性,然后实现一个 Slice 方法,Slice 方法有两个 int 类型的参数,第一个参数表示 offset,第二个参数表示 length

来看下面这个示例吧,还是刚才那个类,我们支持一下 Range

private class TestCollection{    public IList
 Data { get; init; }    //public int[] this[Range range]    //{    //    get    //    {    //        var rangeInfo = range.GetOffsetAndLength(Data.Count);    //        return Data.Skip(rangeInfo.Offset).Take(rangeInfo.Length).ToArray();    //    }    //}    public int Count => Data.Count;    public int[] Slice(int start, int length)    {        var array = new int[length];        for (var i = start; i < length && i < Data.Count; i++)        {            array[i] = Data[i];        }        return array;    }}

More

新的操作符 (^ and ..) 都只是语法糖,本质上是调用 IndexRange

Index 并不是支持负数索引,从最后向前索引只是编译器帮我们做了一个转换,转换成从前到后的索引值,借助于它,我们很多取集合最后一个元素的写法就可以大大的简化了,就可以从原来的 array[array.Length-1] => array[^1]

Range 在创建某个数组的子序列的时候就非常的方便, newArray = array[1..3],需要注意的是,Range 是"左闭右开"的,包含左边界的值,不包含右边界的值

还没使用过 Index/Range 的快去体验一下吧,用它们优化数组的操作吧~~

References

  • https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/ranges-indexes

  • https://docs.microsoft.com/en-us/dotnet/api/system.index?view=net-5.0

  • https://docs.microsoft.com/en-us/dotnet/api/system.range?view=net-5.0

  • https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/ranges

  • https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Index.cs

  • https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Range.cs

  • https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/ArraySegment.cs

转载地址:http://saudi.baihongyu.com/

你可能感兴趣的文章
驱动TFT要SDRAM做为显示缓存
查看>>
使用file查看可执行文件的平台性,x86 or arm ?
查看>>
qt5 everywhere 编译summary
查看>>
qt5 everywhere编译完成后,找不到qmake
查看>>
arm-linux开机读取硬件时钟,设置系统时钟。
查看>>
交叉编译在x86上调试好的qt程序
查看>>
/dev/input/event0 键盘输入
查看>>
qt 创建异形窗体
查看>>
可重入函数与不可重入函数
查看>>
简单Linux C线程池
查看>>
内存池
查看>>
输入设备节点自动生成
查看>>
opencv test code-1
查看>>
eclipse 导入先前存在的项目
查看>>
GNU hello代码分析
查看>>
Qt继电器控制板代码
查看>>
busybox passwd修改密码
查看>>
wpa_supplicant控制脚本
查看>>
rfkill: WLAN hard blocked
查看>>
gstreamer相关工具集合
查看>>