C#学习教程:C#+无unsafe的非托管大数组示例详解(large unmanaged array in c# without ‘unsafe’ keyword)分享

C#申请一个大数组(UsealargearrayinC#)

在C#里,有时候我需要能够申请一个很大的数组、使用之、然后立即释放其占用的内存。

SometimesIneedtoallocatealargearray,useitandthenreleaseitsmemoryspaceimmediately.

由于在C#里提供的int[]array=newint[1000000];这样的数组,其内存释放很难由程序员完全控制,在申请一个大数组后,程序可能会变得很慢。

IfIusesomethinglike int[]array=newint[1000000];,itwillbedifficulttoreleaseitsmemoryspacebyprogrammerandtheappprobablyrunsslowerandslower.

特别是在C#+OpenGL编程中,我在使用VAO/VBO时十分需要设计一个非托管的数组,比如在glBufferData时我希望可以使用下面的glBufferData:

SpeciallyinC#+OpenGLroutineswhenI’musingVAO/VBO,IneedanunmanagedarrayforglBufferData:

///<summary> ///设置当前VBO的数据。 ///</summary> ///<paramname="target"></param> ///<paramname="data"></param> ///<paramname="usage"></param> publicstaticvoidglBufferData(uinttarget,UnmanagedArrayBasedata,uintusage) { GetDelegateFor<glBufferData>()((uint)target, data.ByteLength,//使用非托管数组 data.Header,//使用非托管数组 (uint)usage); } //... //glBufferData的声明 privatedelegatevoidglBufferData(uinttarget,intsize,IntPtrdata,uintusage);

而在指定VBO的数据时,可能是float、vec3等等类型:

AndthecontentinVBOcanbefloat,vec3andanyotherstructs.

///<summary> ///金字塔的posotionarray. ///</summary> staticvec3[]positions=newvec3[] { newvec3(0.0f,1.0f,0.0f), newvec3(-1.0f,-1.0f,1.0f), //... newvec3(-1.0f,-1.0f,1.0f), }; //Createavertexbufferforthevertexdata. { uint[]ids=newuint[1]; GL.GenBuffers(1,ids); GL.BindBuffer(GL.GL_ARRAY_BUFFER,ids[0]); //使用vec3作为泛型的非托管数组的参数 UnmanagedArray<vec3>positionArray=newUnmanagedArray<vec3>(positions.Length); for(inti=0;i<positions.Length;i++) { //使用this[i]这样的索引方式来读写非托管数组的元素 positionArray[i]=positions[i]; } GL.BufferData(BufferDataTarget.ArrayBuffer,positionArray,BufferDataUsage.StaticDraw); GL.VertexAttribPointer(positionLocation,3,GL.GL_FLOAT,false,0,IntPtr.Zero); GL.EnableVertexAttribArray(positionLocation); } UnmanagedArray<T>

所以我设计了这样一个非托管的数组类型:无unsafe,可接收任何struct类型作为泛型参数,可随时释放内存。

SoIdesignedthisUnmangedArray<T>:no’unsafe’keyword,takesanystructasgenericparameter,canbereleasedanytimeyouwant.

1///<summary> 2///元素类型为sbyte,byte,char,short,ushort,int,uint,long,ulong,float,double,decimal,bool或其它struct的非托管数组。 3///<para>不能使用enum类型作为T。</para> 4///</summary> 5///<typeparamname="T">sbyte,byte,char,short,ushort,int,uint,long,ulong,float,double,decimal,bool或其它struct,不能使用enum类型作为T。</typeparam> 6publicclassUnmanagedArray<T>:UnmanagedArrayBasewhereT:struct 7{ 8 9///<summary> 10///元素类型为sbyte,byte,char,short,ushort,int,uint,long,ulong,float,double,decimal,bool或其它struct的非托管数组。 11///</summary> 12///<paramname="count"></param> 13[MethodImpl(MethodImplOptions.Synchronized)] 14publicUnmanagedArray(intcount) 15:base(count,Marshal.SizeOf(typeof(T))) 16{ 17} 18 19///<summary> 20///获取或设置索引为<paramrefname="index"/>的元素。 21///</summary> 22///<paramname="index"></param> 23///<returns></returns> 24publicTthis[intindex] 25{ 26get 27{ 28if(index<0||index>=this.Count) 29thrownewIndexOutOfRangeException("indexofUnmanagedArrayisoutofrange"); 30 31varpItem=this.Header+(index*elementSize); 32//varobj=Marshal.PtrToStructure(pItem,typeof(T)); 33//Tresult=(T)obj; 34Tresult=Marshal.PtrToStructure<T>(pItem);//worksin.net4.5.1 35returnresult; 36} 37set 38{ 39if(index<0||index>=this.Count) 40thrownewIndexOutOfRangeException("indexofUnmanagedArrayisoutofrange"); 41 42varpItem=this.Header+(index*elementSize); 43//Marshal.StructureToPtr(value,pItem,true); 44Marshal.StructureToPtr<T>(value,pItem,true);//worksin.net4.5.1 45} 46} 47 48///<summary> 49///按索引顺序依次获取各个元素。 50///</summary> 51///<returns></returns> 52publicIEnumerable<T>GetElements() 53{ 54if(!this.disposed) 55{ 56for(inti=0;i<this.Count;i++) 57{ 58yieldreturnthis[i]; 59} 60} 61} 62} 63 64///<summary> 65///非托管数组的基类。 66///</summary> 67publicabstractclassUnmanagedArrayBase:IDisposable 68{ 69 70///<summary> 71///数组指针。 72///</summary> 73publicIntPtrHeader{get;privateset;} 74 75///<summary> 76///元素数目。 77///</summary> 78publicintCount{get;privateset;} 79 80///<summary> 81///单个元素的字节数。 82///</summary> 83protectedintelementSize; 84 85///<summary> 86///申请到的字节数。(元素数目*单个元素的字节数)。 87///</summary> 88publicintByteLength 89{ 90get{returnthis.Count*this.elementSize;} 91} 92 93 94///<summary> 95///非托管数组。 96///</summary> 97///<paramname="elementCount">元素数目。</param> 98///<paramname="elementSize">单个元素的字节数。</param> 99[MethodImpl(MethodImplOptions.Synchronized)] 100protectedUnmanagedArrayBase(intelementCount,intelementSize) 101{ 102this.Count=elementCount; 103this.elementSize=elementSize; 104 105intmemSize=elementCount*elementSize; 106this.Header=Marshal.AllocHGlobal(memSize); 107 108allocatedArrays.Add(this); 109} 110 111privatestaticreadonlyList<IDisposable>allocatedArrays=newList<IDisposable>(); 112 113///<summary> 114///立即释放所有<seecref="UnmanagedArray"/>。 115///</summary> 116[MethodImpl(MethodImplOptions.Synchronized)] 117publicstaticvoidFreeAll() 118{ 119foreach(variteminallocatedArrays) 120{ 121item.Dispose(); 122} 123allocatedArrays.Clear(); 124} 125 126~UnmanagedArrayBase() 127{ 128Dispose(); 129} 130 131#regionIDisposableMembers 132 133///<summary> 134///InternalvariablewhichchecksifDisposehasalreadybeencalled 135///</summary> 136protectedBooleandisposed; 137 138///<summary> 139///Releasesunmanagedand-optionally-managedresources 140///</summary> 141///<paramname="disposing"><c>true</c>toreleasebothmanagedandunmanagedresources;<c>false</c>toreleaseonlyunmanagedresources.</param> 142protectedvoidDispose(Booleandisposing) 143{ 144if(disposed) 145{ 146return; 147} 148 149if(disposing) 150{ 151//Managedcleanupcodehere,whilemanagedrefsstillvalid 152} 153//Unmanagedcleanupcodehere 154IntPtrptr=this.Header; 155 156if(ptr!=IntPtr.Zero) 157{ 158this.Count=0; 159this.Header=IntPtr.Zero; 160Marshal.FreeHGlobal(ptr); 161} 162 163disposed=true; 164} 165 166///<summary> 167///Performsapplication-definedtasksassociatedwithfreeing,releasing,orresettingunmanagedresources. 168///</summary> 169publicvoidDispose() 170{ 171this.Dispose(true); 172GC.SuppressFinalize(this); 173} 174 175#endregion 176 177} UnmanagedArray 如何使用(Howtouse)

UnmanagedArray<T>使用方式十分简单,就像一个普通的数组一样:

UsingUnamangedAray<T>isjustlikeanormalarray(int[],vec3[],etc.):

internalstaticvoidTypicalScene() { constintcount=100; //测试float类型 varfloatArray=newUnmanagedArray<float>(count); for(inti=0;i<count;i++) { floatArray[i]=i; } for(inti=0;i<count;i++) { varitem=floatArray[i]; if(item!=i) {thrownewException();} } //测试int类型 varintArray=newUnmanagedArray<int>(count); for(inti=0;i<count;i++) { intArray[i]=i; } for(inti=0;i<count;i++) { varitem=intArray[i]; if(item!=i) {thrownewException();} } //测试bool类型 varboolArray=newUnmanagedArray<bool>(count); for(inti=0;i<count;i++) { boolArray[i]=i%2==0; } for(inti=0;i<count;i++) { varitem=boolArray[i]; if(item!=(i%2==0)) {thrownewException();} } //测试vec3类型 varvec3Array=newUnmanagedArray<vec3>(count); for(inti=0;i<count;i++) { vec3Array[i]=newvec3(i*3+0,i*3+1,i*3+2); } for(inti=0;i<count;i++) { varitem=vec3Array[i]; varold=newvec3(i*3+0,i*3+1,i*3+2); if(item.x!=old.x||item.y!=old.y||item.z!=old.z) {thrownewException();} } //测试foreach foreach(variteminvec3Array.GetElements()) { Console.WriteLine(item); } //释放此数组占用的内存,这之后就不能再使用vec3Array了。 vec3Array.Dispose(); //立即释放所有非托管数组占用的内存,这之后就不能再使用上面申请的数组了。 UnmanagedArrayBase.FreeAll(); } 快速读写UnmanagedArray<T>
UnmanagedArrayHelper

由于很多时候需要申请和使用很大的UnmanagedArray<T>,直接使用this[index]索引方式速度会偏慢,所以我添加了几个辅助方法,专门解决快速读写UnmanagedArray<T>的问题。

publicstaticclassUnmanagedArrayHelper { /////<summary> /////错误1无法获取托管类型(“T”)的地址和大小,或无法声明指向它的指针 /////</summary> /////<typeparamname="T"></typeparam> /////<paramname="array"></param> /////<returns></returns> //publicstaticunsafeT*FirstElement<T>(thisUnmanagedArray<T>array)whereT:struct //{ //varheader=(void*)array.Header; //return(T*)header; //} ///<summary> ///获取非托管数组的第一个元素的地址。 ///</summary> ///<paramname="array"></param> ///<returns></returns> publicstaticunsafevoid*FirstElement(thisUnmanagedArrayBasearray) { varheader=(void*)array.Header; returnheader; } publicstaticunsafevoid*LastElement(thisUnmanagedArrayBasearray) { varlast=(void*)(array.Header+(array.ByteLength-array.ByteLength/array.Length)); returnlast; } ///<summary> ///获取非托管数组的最后一个元素的地址再向后一个单位的地址。 ///</summary> ///<paramname="array"></param> ///<returns></returns> publicstaticunsafevoid*TailAddress(thisUnmanagedArrayBasearray) { vartail=(void*)(array.Header+array.ByteLength); returntail; } } 如何使用

这个类型实现了3个扩展方法,可以获取UnmanagedArray<T>的第一个元素的位置、最后一个元素的位置、最后一个元素+1的位置。用这种unsafe的方法可以实现C语言一样的读写速度。

下面是一个例子。用unsafe的方式读写UnmanagedArray<T>,速度比this[index]方式快10到70倍。

publicstaticvoidTypicalScene() { intlength=1000000; UnmanagedArray<int>array=newUnmanagedArray<int>(length); UnmanagedArray<int>array2=newUnmanagedArray<int>(length); longtick=DateTime.Now.Ticks; for(inti=0;i<length;i++) { array[i]=i; } longtotalTicks=DateTime.Now.Ticks-tick; tick=DateTime.Now.Ticks; unsafe { int*header=(int*)array2.FirstElement(); int*last=(int*)array2.LastElement(); int*tailAddress=(int*)array2.TailAddress(); intvalue=0; for(int*ptr=header;ptr<=last/*or:ptr<tailAddress*/;ptr++) { *ptr=value++; } } longtotalTicks2=DateTime.Now.Ticks-tick; Console.WriteLine("ticks:{0},{1}",totalTicks,totalTicks2);//unsafemethodworksfaster. for(inti=0;i<length;i++) { if(array[i]!=i) { Console.WriteLine("somethingwronghere"); } if(array2[i]!=i) { Console.WriteLine("somethingwronghere"); } } array.Dispose(); array2.Dispose(); } unsafe { vec3*header=(vec3*)vec3Array.FirstElement(); vec3*last=(vec3*)vec3Array.LastElement(); vec3*tailAddress=(vec3*)vec3Array.TailAddress(); inti=0; for(vec3*ptr=header;ptr<=last/*or:ptr<tailAddress*/;ptr++) { *ptr=newvec3(i*3+0,i*3+1,i*3+2); i++; } i=0; for(vec3*ptr=header;ptr<=last/*or:ptr<tailAddress*/;ptr++,i++) { varitem=*ptr; varold=newvec3(i*3+0,i*3+1,i*3+2); if(item.x!=old.x||item.y!=old.y||item.z!=old.z) {thrownewException();} } } 2015-08-25
用StructLayout和MarshalAs支持复杂的struct

在OpenGL中我需要用UnmanagedArray<mat4>,其中mat4定义如下:

1///<summary> 2///Representsa4x4matrix. 3///</summary> 4[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Size=4*4*4)] 5publicstructmat4 6{ 7///<summary> 8///Getsorsetsthe<seecref="vec4"/>columnatthespecifiedindex. 9///</summary> 10///<value> 11///The<seecref="vec4"/>column. 12///</value> 13///<paramname="column">Thecolumnindex.</param> 14///<returns>Thecolumnatindex<paramrefname="column"/>.</returns> 15publicvec4this[intcolumn] 16{ 17get{returncols[column];} 18set{cols[column]=value;} 19} 20 21///<summary> 22///Getsorsetstheelementat<paramrefname="column"/>and<paramrefname="row"/>. 23///</summary> 24///<value> 25///Theelementat<paramrefname="column"/>and<paramrefname="row"/>. 26///</value> 27///<paramname="column">Thecolumnindex.</param> 28///<paramname="row">Therowindex.</param> 29///<returns> 30///Theelementat<paramrefname="column"/>and<paramrefname="row"/>. 31///</returns> 32publicfloatthis[intcolumn,introw] 33{ 34get{returncols[column][row];} 35set{cols[column][row]=value;} 36} 37 38///<summary> 39///Thecolummsofthematrix. 40///</summary> 41[MarshalAs(UnmanagedType.ByValArray,SizeConst=4)] 42privatevec4[]cols; 43} 44 45///<summary> 46///Representsafourdimensionalvector. 47///</summary> 48[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Size=4*4)] 49publicstructvec4 50{ 51publicfloatx; 52publicfloaty; 53publicfloatz; 54publicfloatw; 55 56publicfloatthis[intindex] 57{ 58get 59{ 60if(index==0)returnx; 61elseif(index==1)returny; 62elseif(index==2)returnz; 63elseif(index==3)returnw; 64elsethrownewException("Outofrange."); 65} 66set 67{ 68if(index==0)x=value; 69elseif(index==1)y=value; 70elseif(index==2)z=value; 71elseif(index==3)w=value; 72elsethrownewException("Outofrange."); 73} 74} 75} mat4

注意:UnmanagedArray<T>支持的struct,T的大小必须是确定的。所以在mat4里我们用[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Size=4*4*4)]指定mat4的大小为4个vec4*4个float*4个字节(每个float)=64字节,并且在privatevec4[]cols;上用[MarshalAs(UnmanagedType.ByValArray,SizeConst=4)]规定了cols的元素数必须是4。之后在vec4上的[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Size=4*4)]不写也可以,因为vec4只有4个简单的float字段,不含复杂类型。

下面是测试用例。

mat4matrix=glm.scale(mat4.identity(),newvec3(2,3,4)); varsize=Marshal.SizeOf(typeof(mat4)); size=Marshal.SizeOf(matrix); UnmanagedArray<mat4>array=newUnmanagedArray<mat4>(1); array[0]=matrix; mat4newMatirx=array[0];//newMatrixshouldbeequaltomatrix array.Dispose();

如果matrix和newMatrix相等,就说明上述Attribute配置正确了。

上述就是C#学习教程:C#+无unsafe的非托管大数组示例详解(large unmanaged array in c# without ‘unsafe’ keyword)分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—猴子技术宅(www.ssfiction.com)

本文来自网络收集,不代表猴子技术宅立场,如涉及侵权请点击右边联系管理员删除。

如若转载,请注明出处:https://www.ssfiction.com/ckf/903525.html

发表评论

邮箱地址不会被公开。 必填项已用*标注