目前,我正在使用ranges这个新的c# 8.0
功能。
到目前为止,我有一个问题。我是否可以从头到尾创建一个range
?
假设我有一个array
,我想通过range
反向打印:
static void Test()
{
int[] array = new int[10];
for (int i = 0; i < 10; i++) array[i] = i;
Range range = ^1..0;
foreach (int v in array[range]) WriteLine(v);
}
但是这给我带来了运行时错误-System.OverflowException: 'Arithmetic operation resulted in an overflow.'
我是否正确,ranges
只允许一个方向(从开始到结束)?
答案 0 :(得分:2)
我找不到任何可能的信息。但是,这似乎不是语言限制,因为我们可以创建类似Range r = 5..1;
的范围,并且不会引发异常,因此它必须是Array[Range]
和其他类型的实现,例如不接受的字符串。这样,您可能可以创建一个接受Range
并执行您想要的操作的方法。
编辑:我制作了以下适用于数组的方法:
static T[] MySlice<T>(T[] array, Range r)
{
//Transforms indexes "from end" to indexes "from start"
if(r.Start.ToString()[0] == '^'){
var startIdx = array.Length - r.Start.Value;
r = new Range(startIdx,r.End);
}
if(r.End.ToString()[0] == '^'){
var endIdx = array.Length - r.End.Value;
r = new Range(r.Start,endIdx);
}
//Check if start value is greater than end value. If so, invert it
if(r.Start.Value > r.End.Value)
{
r = new Range(r.End,r.Start);
var invArr = array[r];
Array.Reverse(invArr);
return invArr;
}
return array[r];
}
您可以使用
int[] arr = Enumerable.Range(0,10).ToArray();
Range r = ^0..0;
foreach(var v in MySlice(arr,r)){
Console.WriteLine(v);
}
哪个输出:
9
8
7
6
5
4
3
2
1
0
编辑:
文档说Index
的布尔属性为FromEnd
,它指示索引是否来自结尾。在测试期间,我发现实现的属性名称(在dotnet core 3.0.100-preview3-010431中)为IsFromEnd
。由于dotnet core 3仍处于测试阶段,因此名称可能仍会更改,因此我坚持使用r.Start.ToString()[0] == '^'
来确定Index
是否从头开始,直到现在,直到dotnet core 3的最终版本
答案 1 :(得分:2)
反向范围并不是对Range
类型的限制,而是对Span
(及相关)类型的限制-它们假定为正增量。合理的原因是,数组 slice 的许多低级优化和使用场景仅适用于“指针加大小”的基本概念。 (Link to Range usages in Framework)
虽然这不允许传递反向Span
,但可以使用扩展方法为反向范围枚举提供简单且 performant 语法:
public static class RangeExtensions
{
public static int Normalize(this Index index, int length) =>
index.FromEnd ? length - index.Value : index.Value;
public static (int start, int end) Normalize(this Range range, int length) =>
(range.Start.Normalize(length), range.End.Normalize(length));
public static IEnumerable<T> Enumerate<T>(this T[] items, Range range)
{
var (start, end) = range.Normalize(items.Length);
return start <= end ? items[range] : GetRangeReverse();
IEnumerable<T> GetRangeReverse()
{
for (int i = start; i >= end; i--)
yield return items[i];
}
}
}
...
var numbers = Enumerable.Range(0, 10).ToArray();
foreach (var num in numbers.Enumerate(^3..1))
Console.WriteLine(num);
答案 2 :(得分:0)
int[] array = { 1, 2, 3, 4, 5, 6, 7 };
var range = array[0..5];
Console.WriteLine(string.Join(",", range));
var rev = range.AsEnumerable().Reverse();
Console.WriteLine(string.Join(",", rev));
会产生
1,2,3,4,5
5,4,3,2,1
我相信这需要C#8.0和System.Linq