前言

  如果需要使用相同的类型的多个对象,就可以使用集合和数组,这一节主要讲解数组,其中会重点涉及到Span<T>结构和ArrayPool数组池。我们也会先涉及到简单的数组、多维数组、锯齿数组、Array类。

简单的数组、多维数组、锯齿数组

  简单的数组介绍

  数组的声明:

Int [] myArray;

  初始化:

myArray=new int[];

  还可以:

Int [] myArray=new int []{,,,};

  访问数组:

myArray[];

  多维数组介绍

  一般的数组(也称一维数组)是用一个数字来索引,多维数组用两个或两个以上的数字进行索引。

  声明多维数组时中间以,隔开,我们下面声明一个二维数组。

 int  [,]  twodim=new  int [,]
int[,] twodim = {
{ ,,},
{ ,,},
{ ,,}
};

  一个三维数组。

int[,,] threedim = {
{ { ,},{ ,} },
{{ ,},{ ,} },
{ { ,},{ ,} }
};
Console.WriteLine(threedim[,,]);

  锯齿数组

  二维数组图形:

  锯齿数组

  在声明锯齿数组的时候要依次放置左右括号。在初始化锯齿数组时,只对第一对方括号中设置该数组包含的行数,定义各行中元素个数的第二个方括号设为空,因为这类数组的每一行包含不同的元素个数。

   int[][] jagged = new int[][];
jagged[] = new int[] { , };
jagged[] = new int[] { , , , };
jagged[] = new int[] { , , };

Array类

  创建数组:

Array intArray1 = Array.CreateInstance(typeof(int), );
for (int i = ; i < intArray1.Length; i++)
{
intArray1.SetValue(, i);
  }

  上面这段代码,描述了Array数组的创建以及设置值。CreateInstance()方法第一个参数为元素的类型,第二个参数为定义数组的大小。SetValue()方法设置值第一个参数为设置IDE值,第二个参数为设置的索引。

  复制数组:

int[] intArray1 = { ,};
int[] intArray2 = (int[])intArray1.Clone();

  因为数组是引用类型的,所以将一个数组的变量赋予另一个数组变量,就会得到两个引用同一个数组的变量,这是使用的是Clone()方法创建数组的浅表副本。使用Copy()方法也可以创建浅表副本,Clone()方法会创建一个数组,而Copy()方法必须传递阶数相同且有足够元素的已有数组。

  排序:

class Program
{
static void Main(string[] args)
{
int[] list = { , , , , , , }; Console.Write("原始数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine(); // 逆转数组
Array.Reverse(list);
Console.Write("逆转数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine(); // 排序数组
Array.Sort(list);
Console.Write("排序数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine();
} }

  输出:

原始数组:       

逆转数组:       

排序数组:       

  在上述方法中Array.Sort()方法实现了数组的排序,而Array.Reverse()实现了数组的逆转 。

ArrayPool数组池

  接下来重点来了,本文的重点一,ArrayPool数组池。如果一个应用需要创建和销毁许多的数组,垃圾收集器就要花费很多的功夫来做这些工作,为了较少垃圾收集器的工作,这里我们可以使用ArrayPool类来使用数组池。ArrayPool管理一个数组池,数组可以再这里租借内存,并且返回到这里。需要引用using System.Buffers;

  创建数组池:

ArrayPool<int> arrayPool = ArrayPool<int>.Create(maxArrayLength:, maxArraysPerBucket: );

  maxArrayLength的默认值是1024*10224字节(数组的长度),maxArraysPerBucket默认值是50(数组的数量)。

  这里还可以使用以下方法来使用预定义的共享池。

ArrayPool<int> sharePool = ArrayPool<int>.Shared;

  下面我们就一起看看如何去使用这个数组池吧:

 

class Program
{
static void Main(string[] args)
{
//定义数组池
ArrayPool<int> arrayPool = ArrayPool<int>.Create(maxArrayLength: , maxArraysPerBucket: ); //定义使用数组的长度
int arrayLenght = ;
int[] array = arrayPool.Rent(arrayLenght); //输出数组的长度
Console.WriteLine($"定义数组长度:{arrayLenght},实际数组长度:{array.Length}"); //对数组进行赋值
array[] = ;
array[] = ;
array[] = ;
array[] = ;
array[] = ; //输出数组的值
foreach (var item in array)
{
Console.WriteLine(item);
} //将内存返回给数组池,clearArray设置True清除数组,下次调用时为空,设置False,保留数组,下次调用还是现在的值
arrayPool.Return(array, clearArray: true);
foreach (var item in array)
{
Console.WriteLine(item);
} }
}

  在上面事例中,我们使用Rent()方法请求池中的内存,Rent方法返回一个数组,其中至少包含所请求的元素个数。返回的数组可能会用到更多的内存,池中最少的请求为16个元素,紧接着是32,64,128以此类推。所以在上述例子中我们请求的长度为5,但是实际使用的元素个数为16个,多余的将根据类型对其赋值0或者null。

  我们使用Return()方法将数组返回到池中,这里使用了一个可选参数clearArray,指定是否清除该数组,不清除的话下一个从池中租用这个数组的人可以读取到其中的数据。清除数据可以避免这种情况,但是会消耗更多的CPU时间。

Span<T>

  Span<T>介绍

  为了快速访问托管或非托管的连续内存,可以使用Spam<T>结构。一个可以使用Span<T>结构的例子就是数组,Span<T>结构在后台保存在连续的内存中,另一个例子就是长字符串。

  使用Span<T>结构,可以直接访问数组元素。数组的元素没有复制,但是它们可以直接调用,并且比复制还快。

class Program
{
static void Main(string[] args)
{
int[] arr1 = { , , , , , , , , };
var span1 = new Span<int>(arr1);
span1[] = ;
Console.WriteLine(arr1[]);
}
}

  输出:

  这里将创建的arr1数组传递给Span<T>,同时Span<T>类型提供了一个索引器,这里直接修改span1的第二个值,然后再输出arr1数组中的第二个值,也是被其修改过得值。

  Span<T>切片

  Span<T>它一个强大的特性是,可以使用它访问数组的部分或者切片,使用切片的时候不会复制数组元素,他们是从Span中直接访问的。下面代码介绍了创建切片的两种方法:

class Program
{
static void Main(string[] args)
{
//定义简单的数组
int[] arr2 = { , , , , , , , , , , , , , }; //Span<T>对数组进行切片,访问arr2数组,从第三个开始,取长度6个的一个数组。
var span3 = new Span<int>(arr2, start: , length: ); //输出切片中的值
foreach (var item in span3)
{
Console.WriteLine(item);
}
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine(""); //对span3进行切片处理,从第二个开始,去长度4个的一个数组
var span4 = span3.Slice(start: , length: ); foreach (var item in span4)
{
Console.WriteLine(item);
}
}
}

  输出:

  使用Span<T>改变值

  前面介绍了如何使用Span<T>的索引器,更改数组的元素,下面介绍的将会有更多的选项,关于修改元素的值及复制。

class Program
{
static void Main(string[] args)
{
//定义简单的数组
int[] arr = { , , , , , , , , , , , , , }; //将数组传递给span
var span = new Span<int>(arr);
var span2 = new Span<int>(arr); //对span进行切片处理,从第四个开始
var span3 = span.Slice(start: ); //调用clear方法,用0填充span3
span3.Clear();
foreach (var item in span3)
{
Console.WriteLine("span3的值:"+item);
}
Console.WriteLine("span3的长度:"+span3.Length); //创建新的切片span4,从span2开始,长度3
Span<int> span4 = span2.Slice(start: , length: ); //调用Fill方法,用传入的值填充span4
span4.Fill(); foreach (var item in span4)
{
Console.WriteLine("span4的值"+item);
}
Console.WriteLine("span4的长度"+span4.Length); //将span4复制给span,复制失败
span4.CopyTo(span); //将span复制给span3 复制失败
if (!span.TryCopyTo(span3))
{
Console.WriteLine("复制不了");
}
}
}

  输出:

  上面事例中,显示调用clear()方法,该方法用0填充Span,然后调用了Fill()方法,该方法用传递给Fill方法的值来填充Span,同时也可以将一个Span<T>复制给另一个Span<T>,这里先是采用的CopyTo,在这个方法中,如果另一个目标span不够大,就会复制失败,这里可以使用TryCopyTo来优化此功能,如果目标不够大,将会返回false。以此来判断是否复制成功。上面例子中span4长度为3,而span长度为14,这里是复制成功了,然后其下面的操作,因为span3的长度是10,span复制给span3失败了。因为span3不够大。

  上面事例中提供了改变值的一些方法,当我们不需要对值进行改变,只需要对数组进行读访问的时候,我们可以使用ReadOnlySpan<T>。这里定义了只读的Span

 ReadOnlySpan<int> readonlySpan = new ReadOnlySpan<int>(arr);

总结

  在本篇文章中,重点介绍了ArrayPool数组池和Span<T>结构,通过使用数组池,来降低数组创建和销毁时消耗的性能,减少垃圾回收器的工作,使用Span<T>可以快速的访问托管及非托管代码,创建切片来对数组和长字符串进行一定的操作。更加高效的操作数组。

 

  青少年是一个美好而又是一去不可再得的时期,是将来一切光明和幸福的开端。——加里宁

  我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2n1gp5jweqww4


                      c#基础知识详解系列

  欢迎大家扫描下方二维码,和我一起学习更多的C#知识 

  

数组(ArrayPool数组池、Span<T>结构)的更多相关文章

  1. ArrayPool数组池、Span<T>结构

    数组(ArrayPool数组池.Span<T>结构) 目录 前言 简单的数组.多维数组.锯齿数组 Array类 ArrayPool数组池 Span Span介绍 Span切片 使用Span ...

  2. [Go] 复合类型(数组、切片、字典、结构体)变量的 初始化 及 注意事项

    Go变量 初始化 对 复合类型(数组.切片.字典.结构体)变量的初始化是,有一些语法限制: 1.初始化表达式必须包含类型标签: 2.左花括号必须在类型尾部,不能另起一行: 3.多个成员初始值以逗号分隔 ...

  3. C语言定义结构体指针数组并初始化;里面全是结构体的地址

    #include <stdio.h> #include <string.h> struct tells;//声明结构体 struct info { char *infos; } ...

  4. C/C++知识总结 五 复合数据类型 壹(数组、字符串与string、结构)

    C/C++复合数据类型 壹(数组.结构) 数组 数组的意义.定义与创建 一.二维数组应用 字符数组与字符串处理函数 数组与指针---关系密切 数组的意义与定义创建 意义:反映数据间的特点(通过把同一类 ...

  5. C++中,指针数组和数组指针

    这俩兄弟长得实在太像,以至于经常让人混淆.然而细心领会和甄别就会发现它们大有不同. 前者是指针数组,后者是指向数组的指针.更详细地说. 前: 指针数组;是一个元素全为指针的数组. 后: 数组指针;可以 ...

  6. c指针与数组,传参问题,指针数组与数组指针的区别,二维数组动态内存分配

    一 数组的结构:顺序存储,看谭浩强中的图,牢记 1.数组名指代一种数据结构:数组 现在可以解释为什么第1个程序第6行的输出为10的问题,根据结论1,数组名str的内涵为一种数据结构,即一个长度为10的 ...

  7. c语言指针数组与数组指针

    一.指针数组和数组指针的内存布局初学者总是分不出指针数组与数组指针的区别.其实很好理解:指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定.它是“储存指针的数组”的简称.数 ...

  8. [C++ Primer Plus] 第7章、函数(一)程序清单——递归,指针和const,指针数组和数组指针,函数和二维数组

    程序清单7.6 #include<iostream> using namespace std; ; int sum_arr(int arr[], int n);//函数声明 void ma ...

  9. C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序

    C#中缓存的使用   缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可:  <%@ Outp ...

随机推荐

  1. <iOS小技巧> 昵称格式判断

    一.使用方式 + 如下代码块功能:判断字体,判断字体输入格式       NSString *firstStr = [name substringToIndex:1];    NSArray *num ...

  2. 利用js参数,保持客户端文件的新鲜度

    不知道你是否碰到过如下情况,在服务端更新了一个重要的js文件后,由于浏览器的缓存机制,导致用户始终不能获取到最新的文件,此时的你恨不得有孙悟空吹毛化身的法术,帮用户清除浏览器的缓存.缓存既是程序员的好 ...

  3. 地球坐标-火星坐标-百度坐标及之间的转换算法 C#

    美国GPS使用的是WGS84的坐标系统,以经纬度的形式来表示地球平面上的某一个位置.但在我国,出于国家安全考虑,国内所有导航电子地图必须使 用国家测绘局制定的加密坐标系统,即将一个真实的经纬度坐标加密 ...

  4. Java集合 ArrayList原理及使用

    ArrayList是集合的一种实现,实现了接口List,List接口继承了Collection接口.Collection是所有集合类的父类.ArrayList使用非常广泛,不论是数据库表查询,exce ...

  5. 使用 Cake 推送 NuGet 包到 AzureDevops 的 Artifacts 上

    前言 大家好,我最近在想如何提交代码的时候自动的打包 NuGet 然后发布到 AzureDevOps 中的 Artifacts,在这个过程中踩了很多坑,也走了很多弯路,所以这次篇文章就是将我探索的结果 ...

  6. mvc中Scripts.Render的用法

    第一次接触新的东西,都会很陌生,但是时间久了就熟悉了变简单了. 视图文件中使用Scripts.Render()输出脚本包,Styles.Render()输出样式包 上面两张图是我所做项目里的,放上面会 ...

  7. git push 时:报missing Change-Id in commit message footer的错误

    1. 一般而言,按照提示执行以下两个命令即可生成新的Change-id - gitdir=$(git rev-parse --git-dir); scp -p -P 29418 guan@192.16 ...

  8. Java 8 新特性-Stream更优雅的处理集合入门

    Java 8 新特性之--Stream 一. 简单介绍 Stream是Java 8提出了的一种新的对集合对象功能的增强.它集合Lambda表达式,对集合提供了一些非常便利,高效的操作,使得代码具有非常 ...

  9. GRPC Oauth Identity

    gRPC中集成asp.net identity实现oAuth认证 在asp.net core 3.0中开启identity认证 asp.net core 3.0种需要导入的identity包与core ...

  10. Requests方法 -- cookie绕过验证码登录操作

    前言有些登录的接口会有验证码:短信验证码,图形验证码等,这种登录的话验证码参数可以从后台获取的(或者查数据库最直接).获取不到也没关系,可以通过添加 cookie 的方式绕过验证码. 1.这里以登录博 ...