There’s sliced grammar candy in C #. It’s amazing

Time:2021-2-20

1: Background

1. Tell a story

Yesterday on GitHub, I was going to find out what new grammars C # 9 has to try. I found a strange way to write it in a documentforeach (var item in myArray[0..5])Ha ha, familiar and strange, friends who have played Python are interested in this[0..5]I’m too familiar with it. I even met it in c#. I’m happy. I saw the new grammar of c#8. It’s ironic. I made 9 before I was familiar with 8. I have a strong desire to explore. I always want to see what the bottom of this thing is supported by.

2: The usage of sugar

From the previous introductionmyArray[0..5]Semantically, we can see that this is an operation to segment array. How many segmentation methods are there? To facilitate the demonstration, I first define an array. The code is as follows:


var myarr = new string[] { "10", "20", "30", "40", "50", "60", "70", "80", "90", "100" };

1. Extract the first three elements of arr

If you use LINQ, you can use take (3). If you use slicing, it is [0.. 3]. The code is as follows:

static void Main(string[] args)
        {
            var myarr = new string[] { "10", "20", "30", "40", "50", "60", "70", "80", "90", "100" };

            //1. Get the first three elements of the array
            var query1 = myarr[0..3];

            var query2 = myarr.Take(3).ToList();

            Console.WriteLine($"query1={string.Join(",", query1)}");
            Console.WriteLine($"query2={string.Join(",", query2)}");
        }

There's sliced grammar candy in C #. It's amazing

2. Extract the last three elements of arr

How to extract this? In Python, you can use – 3 directly. In C #, you need to use ^, starting from the end. The code is as follows:

static void Main(string[] args)
        {
            var myarr = new string[] { "10", "20", "30", "40", "50", "60", "70", "80", "90", "100" };

            //1. Get the last three elements of the array
            var query1 = myarr[^3..];

            var query2 = myarr.Skip(myarr.Length - 3).ToList();

            Console.WriteLine($"query1={string.Join(",", query1)}");
            Console.WriteLine($"query2={string.Join(",", query2)}");
        }

There's sliced grammar candy in C #. It's amazing

3. Extract the three position elements of index = 4, 5 and 6 in the array

If you use LINQ, you need to use itSkip + TakeDouble combination, if you use slicing operation, it’s too simple…

static void Main(string[] args)
        {
            var myarr = new string[] { "10", "20", "30", "40", "50", "60", "70", "80", "90", "100" };

            //1. Get the elements of index = 4, 5 and 6 in the array
            var query1 = myarr[4..7];

            var query2 = myarr.Skip(4).Take(3).ToList();

            Console.WriteLine($"query1={string.Join(",", query1)}");
            Console.WriteLine($"query2={string.Join(",", query2)}");
        }

There's sliced grammar candy in C #. It's amazing

Cut from above[4..7]According to the output, this is a problemLeft closed right openSo pay special attention.

4. Get the penultimate and second elements in the array

If you understand the previous two usages, I believe you will write them quickly. The code is as follows:

static void Main(string[] args)
        {
            var myarr = new string[] { "10", "20", "30", "40", "50", "60", "70", "80", "90", "100" };

            //1. Get the penultimate and second elements in the array
            var query1 = myarr[^3..^1];

            var query2 = myarr.Skip(myarr.Length - 3).Take(2).ToList();

            Console.WriteLine($"query1={string.Join(",", query1)}");
            Console.WriteLine($"query2={string.Join(",", query2)}");
        }

There's sliced grammar candy in C #. It's amazing

3、 Explore the principle

Through the first four examples, I think we all know how to play. The next step is to see what supports are used inside. Here we use dnspy to dig.

1. From myarr [0.. 3]

The code is as follows:

//Before Compilation
    var query1 = myarr[0..3];

    //After compilation:
    string[] query = RuntimeHelpers.GetSubArray<string>(myarr, new Range(0, 3));

From the compiled code, we can see that the original array to get the slice is the callRuntimeHelpers.GetSubArrayYes, and I’ll simplify the method. The code is as follows:


        public static T[] GetSubArray<[Nullable(2)] T>(T[] array, Range range)
        {
            ValueTuple<int, int> offsetAndLength = range.GetOffsetAndLength(array.Length);
            int item = offsetAndLength.Item1;
            int item2 = offsetAndLength.Item2;
            T[] array3 = new T[item2];
            Buffer.Memmove<T>(Unsafe.As<byte, T>(array3.GetRawSzArrayData()), Unsafe.Add<T>(Unsafe.As<byte, T>(array.GetRawSzArrayData()), item), (ulong)item2);
            return array3;
        }

As you can see from the above code, the last subarray is created byBuffer.MemmoveBut the cutting position given to the subarray is determined by theGetOffsetAndLengthMethod implementation, continue to follow the code:


    public readonly struct Range : IEquatable<Range>
    {   
        public Index Start { get; }
        public Index End { get; }

        public Range(Index start, Index end)
        {
            this.Start = start;
            this.End = end;
        }

        public ValueTuple<int, int> GetOffsetAndLength(int length)
        {
            Index start = this.Start;
            int num;
            if (start.IsFromEnd)
            {
                num = length - start.Value;
            }
            else
            {
                num = start.Value;
            }
            Index end = this.End;
            int num2;
            if (end.IsFromEnd)
            {
                num2 = length - end.Value;
            }
            else
            {
                num2 = end.Value;
            }
            return new ValueTuple<int, int>(num, num2 - num);
        }
    }

After reading the above code, you may have two doubts:

1) start.IsFromEnd And end.IsFromEnd What do you mean?

In fact, after reading the above code logic, you can see that isfromend indicates whether the starting point starts from the left or from the right, which is so simple.

2) I didn’t see it start.IsFromEnd And end.IsFromEnd How to assign value.

In the constructor of the index class, it depends on how the previous layer inserts true or false in the new index. The code is as follows:

There's sliced grammar candy in C #. It's amazing

The process of this example is as follows:new Range(1,3) -> operator Index(int value) -> FromStart(value) -> new Index(value)You can see that the optional parameters are not assigned at the end of new.

2. Explore myarr1

In the example just now, the optional parameter is not assigned. Let’s see if the new index is assigned in this example?

//Before compiling:
var query1 = myarr[^3..];

//After compilation:
string[] query = RuntimeHelpers.GetSubArray<string>(myarr, Range.StartAt(new Index(3, true)));

See, this time in the new index, isfromend = true is given, which means that the calculation starts from the end. Combined with the getoffsetandlength method, I think you should straighten out the logic.

4: Summary

Generally speaking, this slicing operation is too practical. It can greatly reduce the use of skip & take for arr and substring for string"12345"[1..3] -> "12345".Substring(1, 2)Hey, hey, that’s great! Or c-dafa??

More high quality dry goods: see my GitHub:dotnetfly


  1. 3..

Recommended Today

A detailed explanation of the differences between Perl and strawberry Perl and ActivePerl

Perl is the abbreviation of practical extraction and report language “practical report extraction language”. Application of activestateperl and strawberry PERL on Windows platformcompiler。 Perl   The relationship between the latter two is that C language and Linux system have their own GCC. The biggest difference between activestate Perl and strawberry Perl is that strawberry Perl […]