“不要频繁获释对象”的略随笔

【题外话】

【题外话】

前面大部分日子还于于是Visual Studio
2008召开开发,虽然为触及起了代码分析,但是同看无异特别串内容,尤其是同样格外失误针对命名的提议,就果断关闭了。这次见习使用的Visual
Studio
2012,发现代码分析默认去丢了过多情节,显示的吧都是较关键并需要改善之地方,所以也还信以为真钻研了转。

近期实验室设自修改C3D(The 3D Biomechanics Data
Standard)文件,虽然于网上找到了一个于c3d4sharp的类库,这个类库单纯读取C3D文件的话语还可,但是一旦一旦实现修改要创造C3D文件就比麻烦了。同时c3d4sharp实现得比较简单,很多C3D文件里有数据还不支持。好以C3D文件总体不是颇复杂,于是我就是开始再描绘了一个C3D文件读写的库房,现在当codeplex上创设了个类型给C3D.NET。

 

 

【文章索引】

【文章索引】

  1. 问题与缓解办法
  2. 缘何这样失去开
  3. 连锁链接
  1. C3D文件格式的构造
  2. C3D文件头的布局
  3. C3D文件参数集合的结构
  4. C3D文件数量区域的组织
  5. 以C3D.NET读写文件示例

 

 

【一、问题同化解措施】

【一、C3D文件格式的布局】

应当有人会写如下的代码吧,为了释放资源,我们将开拓的物都关掉,貌似没有啊问题。

率先说C3D文件整体不是老大复杂,也不曾过多复杂的概念,C3D的文档格式可以打那个官网下载或者在线阅读。首先C3D文件是坐Section为单位存储的,每一个Section固定啊512字节。Section一定是准顺序存储的,不过有意思的凡,Section的序号是从1开端之,而休是0。C3D文件分为三有的,分别是Section
ID = 1的C3D文件头(固定为一个Section,512字节),Section
ID一般等2(在文件头内会让出)的C3D参数集合以及Section
ID不明了当几(在文件头与参数集合中还见面叫闹)的C3D数据有。

 1 FileStream fs = null;
 2 StreamReader sr = null;
 3 
 4 try
 5 {
 6     fs = new FileStream(@"F:\test.txt", FileMode.Open, FileAccess.Read);
 7     sr = new StreamReader(fs);
 8 
 9     String content = sr.ReadToEnd();
10 }
11 finally
12 {
13     if (sr != null)
14     {
15         sr.Close();
16     }
17 
18     if (fs != null)
19     {
20         fs.Close();
21     }
22 }

但是C3D也产生坏复杂的地方,一个是关于整型的运,可以下以有号的(Int16),也得以应用无符号的(UInt16),只不过后者能够储存的数据量要多有罢了,既然这样,不知怎么当初还要以闹标志的整型。而且最好要紧的凡,文档内尚未其它标识能指出文档使用的是何种整型。官方给闹底缓解智是,可以根据例如帧总数、帧索引等判定,如果念来负数,则应用无符号的,否则用产生标志的。

本,喜欢用using的同室为可能会见刻画如下的代码:

任何一个凡是C3D文件能够以不同档次的CPU上变化,这表现于不同CPU可能行使的配节序(Endian)和浮点数字不同,比如大家用的CPU都是以Little-Endian以及IEEE754的浮点数标准。从网上查阅还发现来DEC
(VAX)以及IBM等CPU采用不同之浮点数标准,详见我前同一篇稿子:http://www.cnblogs.com/mayswind/p/3365594.html。而C3D则是永葆3类CPU,Intel
CPU采用Little-Endian以及IEEE754标准的浮点数,DEC
(VAX)采用的Little-Endian以及故意的浮点数,MIPS
(SGI)采用的Big-Endian以及IEEE754标准的浮点数,所以于读取文档的时段恐怕需要分外开展处理,在第三节会详细说明。

1 using (FileStream fs = new FileStream(@"F:\test.txt", FileMode.Open, FileAccess.Read))
2 {
3     using (StreamReader sr = new StreamReader(fs))
4     {
5         String content = sr.ReadToEnd();
6     }
7 }

 

但及时片种代码如果利用代码分析会现出什么情况呢,如下图。

【二、C3D文件头之布局】

第一来说第一组成部分,也不怕是C3D的文本头,C3D的公文头一定就占1只Section,即定位的512字节,所以若读取前512字节就可管整个头数据获得到了。虽然每个Section有512字节的多,但是对C3D的文书头就占了好少的同样片,在文件头着起恢宏空白的区域。其中第一有的是文本头参数有,内容如下:

比好玩的是,这里提醒的凡“不承诺针对一个对象往往调用
Dispose”,为什么会是“一个目标”呢?

通过看MSDN中之CA2202(链接以文后),我们好查及由是这般的,“某个方法实现所含有的代码路径可能导致对同对象往往调用
IDisposable.Dispose 或和 Dispose 等效的计(例如,用于某些品种的
Close()
方法)”,MSDN中直接给出了缓解方法就是是毫不关StreamReader,而是径直关闭FileStream。

字节 类型 说明
00H Byte 参数集合开始的Section ID(通常为0x02,但也不一定)
01H Byte 文件标识(固定为0x50)
02H-03H Int16 每帧里3D坐标点的个数
04H-05H Int16 每帧里模拟测量的个数
06H-07H Int16 第1帧的序号(最小为1)
08H-09H Int16 最后1帧的序号
0AH-0BH Int16 最大插值差距
0CH-0FH Single 比例因子(正数表示存储的帧为整数帧,负数为浮点帧)
10H-11H Int16  数据区域开始的Section ID
12H-13H Int16 每帧模拟采样个数
14H-17H Single 帧率

 

在此之后的次有些,也就算是储存的轩然大波,听上应该占据多字节,但是出于限了轩然大波数量极其多不克跳18个,同时事件名称最为丰富呢4字节,所以事件部分为单独占大少之上空。由于C3D主要是为着记录运动的多少,可能当里面起成百上千于主要之地方,事件就用来标记出这些地方的。一个波包括三独内容,分别是无与伦比丰富四字节的事件名称、一字节的风波是否合宜显得的状态与一个四字节底单独精度浮点数表示事件出现的时间。

【二、为什么如此夺做】

字节 类型 说明
12AH-12BH Int16 事件名是否支持4字节(支持为0x3039,不支持为0)
12CH-12DH Int16 事件数量(最大为18)
130H-176H Single[] 按事件顺序存储的每个事件发生的时间(第1个帧为0.0s)
178H-188H Byte[] 按事件顺序存储的每个事件是否应该显示(1为显示,0为不显示)
18CH-1D2H Char[] 按事件顺序存储的每个事件的名称(每个事件占4字节)

MSDN给出之法门为什么而如此做吗?出于好奇心,首先用上述的代码单步调试一下:

 

【三、C3D文件参数集合的布局】 

当实践了StreamReader的Close之后,StreamReader的BaseStream指向了null,同时fs也变为了不可知读取,但fs不是null。

C3D文件存储了大气之参数,其以了接近目录的艺术囤了参数,不过还吓单纯发一级。即参数有只有参数组和参数,并且每个参数组里只能发出参数不可知重复包含参数组,每个参数必须于一个参数组内。参数集合起始于文件头被之率先只字节表示的Section
ID,通常为2,但是也未必然,有的文件会在文书头晚留下出空白,然后参数集合起始之Section
ID就延期了。所以判断是否也C3D文件千万不要同开始读进去个Int16然后判断是免是0x5002,而自然要判第二只字节是无是0x50,确定参数集合的位置为必将要是根据文件之第一字节来。

接下来我们所以Reflector找到StreamReader的实现(在mscorlib.dll文件中)如下:

若于参数集合,开头的4字节定义如下:

 1 public override void Close()
 2 {
 3     this.Dispose(true);
 4 }
 5 
 6 protected override void Dispose(bool disposing)
 7 {
 8     try
 9     {
10         if ((this.Closable && disposing) && (this.stream != null))
11         {
12             this.stream.Close();
13         }
14     }
15     finally
16     {
17         if (this.Closable && (this.stream != null))
18         {
19             this.stream = null;
20             this.encoding = null;
21             this.decoder = null;
22             this.byteBuffer = null;
23             this.charBuffer = null;
24             this.charPos = 0;
25             this.charLen = 0;
26             base.Dispose(disposing);
27         }
28     }
29 }
字节 类型 说明
00H Byte 第一个参数所在的Section在整个参数集合中的位置(通常为0x01,说明开头4字节之后就是第一个参数)
01H Byte 参数集合部分标识(固定为0x50)
02H Byte 参数集合所占Section数量
03H Byte 生成文件的CPU类型(0x54为Intel,0x55为DEC (VAX, PDP-11),0x56为MIPS (SGI/MIPS))

StreamReader在履Close时居然执行了this.stream(BaseStream)的Close,然后以BaseStream再指向null,这就算化解了事先为什么提示不要反复自由
一个
对象,其实是StreamReader的Close已经放了扳平赖而已。当然,不仅仅是StreamReader是这样子,StreamWriter、BinaryReader等等为还是这样子的。

中前2独字节官方说一直忽略就执行,但是以配合于写入的时节还是如描绘进去的。第3字节其实我们仍顺序读到头也非需这数量。这个中首要的是CPU类型,由于不同CPU类型采用的配节序以及存储的浮点数字有所不同,所以我们还用依据CPU类型进行对应的拍卖。

然,为什么MSDN的例子让的是关流一旦非是倒闭读取器呢?

对此Intel和DEC生成的文档,都是行使Little-Endian字节序存储的文档,所以肯定要是以Little-Endian来读取Int16、Single等品种;而MIPS则动用的Big-Endian字节序存储文档,所以于读取的时一定要看清时计算机默认的许节序以及文档采用的配节序。

读书了网上为从未找到权威的素材,所以个人总结了几碰如下仅供参考:

一经对此Intel和MIPS生成的文档,对于浮点数字之贮存都是采取标准的IEEE754浮点数字,对于.NET而言不需要开展其它处理;而DEC生成的文档则用特有浮点数,需要以4单字节全部读取以后重新展开超常规之转移,转换方法见自己前的章:http://www.cnblogs.com/mayswind/p/3365594.html。

1、关闭StreamReader等其实就倒闭了彼BaseStream,但易使开发者误以为BaseStream没有关闭而后续采取导致抛来非常,所以关闭最基础之流会更好把。

以斯之下就囤在有的参数了,参数分为两接近,分别是参数组和参数。

2、StreamReader等自并没利用非托管的情,所以啊不管需积极履行Close,让GC去举行就哼了。

于参数组,要存储以下6单内容:

 

字节 类型 说明
00H SByte 参数组名称长度(如果为负数则表示该参数组锁定请不要修改,而长度为绝对值)
01H SByte 参数组ID的负数
02H – … Char[] 参数组名称(仅包含大写字母、0-9以及下划线_)
… + 1 – … + 2 Int16 下一参数组/参数的偏移(包含本内容的2字节)
… + 3 Byte 参数组描述长度
… + 4 –  Char[] 参数组描述内容(ASCII码)

【三、相关链接】

C3D文件并未确定一个参数组后边和另外一个参数组还是暨该参数组里的享有参数,所以读取的时如果专注下。而参数的内容虽然和参数组基本雷同,只是于产一致参数组/参数的皇与参数组描述长度之间存放着该参数的莫过于数据罢了,由于位置描述起来最为难为了,这里虽不写了。

1、CA2202:不要频繁获释对象:http://msdn.microsoft.com/zh-cn/library/ms182334(v=vs.110).aspx

字节

类型

说明

前面的内容

 

Int16

生一样参数组/参数的晃动(包含本内容之2字节)

 

Byte

参数存放内容之路(-1 Char,1 Byte,2
Int16,4 Single),绝对值即为长

 

Byte

参数内容维数(0-3)

 

Byte[]

参数每一样维大小(如果维数为0,就从来不是有)

 

Byte[] 

参数实际内容

 

Byte

参数组描述长度

之后的情

此用验证的便是,由于参数可以存放数组,所以多了维数的标识,即当维数为0时,存放的情吧Char、Byte、Int16、Single等转移出的字节数组;而当维数为1时不时,存放的为Char[]、Byte[]、Int16[]、Single[]相当于转移出底字节数组,以此类推。而对数组的囤,其实就是数组每个元素依次展开仓储,而于多维数组,则是按行优先开展仓储的,比如三维数组,先囤Data[0,0,1]再存储Data[0,0,2],依次类推。

而是需要征的凡,对于Char[]以及Char[,]旋即片栽,如果表示的语句实际当相应之是String以及String[]。

 

【四、C3D文件数量区域的组织】

C3D数据区域为帧为单位寄放的,其实一定给斯区域便是一个幅的联谊。而C3D帧其实分为两栽,一种植是整数帧,而另一样栽是浮点帧。这两头的区分在于,前者存储的有着情节还是Int16,而后者则也Single,除此之外,前者的3D坐标点(X、Y、Z)还索要倍加比例因子才方可,而后者存储的内容相当给已就以了百分比因子了。

数区域开始于参数集合中之”POINT”参数组中的”DATA_START”参数,其代表数据区域开始的Section
ID,除此之外,在文书头着吗出一样卖副本。不过本合法的布道,如果文件头与参数集合中都有些内容,优先读取参数集合中之数。

对此每个帧,又饱含两单有,第一片也3D坐标点部分,第二组成部分为模拟采样部分。

  • 对于每帧的3D坐标点部分,存储方该帧所有3D坐标点的数据,每个3D坐标点包括4单Int16或Single数据,分别是X坐标、Y坐标、Z坐标以及Residual和Camera
    Mask,其中Residual和Camera
    Mask共占一个Int16。比较有趣的凡,对于浮点帧,Residual和Camera
    Mask仍然为要一个Int16,只不过存储的下要用相应之数值转换为Single再拓展仓储。

    • 对浮点帧,存储的X、Y、Z坐标就是其实的坐标;而对于整数幅,存储的X、Y、Z的坐标还欲倍加比例因子才得,比例因子存储于参数集合中之”POINT”参数组中的”SCALE”参数。
    • Residual和Camera
      Mask共占用一个Int16,将其变为字节数组后,高位字节(第1单字节)的万丈位代表Residual的号子,即表示该坐标点是否管用,如果也0虽说表示中,如果也1虽象征无效,而剩下的7个字节则也Camera
      Mask,每一样各项表示一个摄像机,从低到高位分别表示7独摄像机是否采用(为1乎用,为0啊不以)。而Residual的实事求是数据虽然也字节数组的第0配节乘以比例因子(浮点帧则也比例因子的绝对值)。
  • 倘若仿照采样部分,则存储着该帧所有的模仿采样的数,不过每个帧可能带有多独拟采样,同时每个模仿采样可能同时饱含多只channel,存储的多寡就是为该channel下记录之数量。不过存储的数与事实上的数目还欲依据下述公式进行折算,其中data
    value为存储的数额,real world value为实际的多少。

    • zero
      offset可以起”ANALOG”参数组中之”OFFSET”中赢得,该数据为Int16之数组,第i各拄的就是第i独channel的zero
      offset。
    • channel
      scale可以从”ANALOG”参数组被之”SCALE”中取得,该数据吧Single的屡屡组,第i个据的饶是dii个channel的scale。
    • general
      scale是有着拟采样都亟需倍加的比重,该多少好起”ANALOG”参数组中之”GEN_SCALE”中获取,为Single。

    real world value = (data value – zero offset) channel scale general scale

 

【五、使用C3D.NET读写文件示例】

前说了这样多,其实若就此C3D.NET来分析的言辞实际是非常简单的。大家好自https://c3d.codeplex.com/下载C3D.NET的二进制文件要源码,引用后要的好像都以C3D这个命名空间下。

对此遍历所有的3D坐标可以运用以下的方式,首先得从文本或者由流中创建C3D文件,然后打文本头被读取存储的第1幅的序号,然后读取采样点的数码就可以了,当然为可以不由参数组被读取,直接下file.AllFrames[i].Point3Ds.Length也可以:

 1 C3DFile file = C3DFile.LoadFromFile("文件路径");
 2 Int16 firstFrameIndex = file.Header.FirstFrameIndex;
 3 Int16 pointCount = file.Parameters["POINT:USED"].GetData<Int16>();
 4 
 5 for (Int16 i = 0; i < file.AllFrames.Count; i++)
 6 {
 7     for (Int16 j = 0; j < pointCount; j++)
 8     {
 9         Console.WriteLine("Frame {0} : X = {1}, Y = {2}, Z = {3}",
10             firstFrameIndex + i,
11             file.AllFrames[i].Point3Ds[j].X,
12             file.AllFrames[i].Point3Ds[j].Y ,
13             file.AllFrames[i].Point3Ds[j].Z);
14     }
15 }

设读取模拟采样的话,采用的措施呢近乎:

 1 Single frameRate = file.Parameters["POINT", "RATE"].GetData<Single>();
 2 Int16 analogChannelCount = file.Parameters["ANALOG", "USED"].GetData<Int16>();
 3 Int16 analogSamplesPerFrame = (Int16)(file.Parameters["ANALOG", "RATE"].GetData<Int16>() / frameRate);
 4 
 5 for (Int16 i = 0; i < file.AllFrames.Count; i++)
 6 {
 7     for (Int16 j = 0; j < analogChannelCount; j++)
 8     {
 9         for (Int16 k = 0; k < analogSamplesPerFrame; k++)
10         {
11             Console.WriteLine("Frame {0}, Sample {1} : {2}",
12                 firstFrameIndex + i, j + 1,
13                 file.AllFrames[i].AnalogSamples[j][k]);
14         }
15     }
16 }

除开一次性将C3D文件内容全读取出来的这种方法外,还可以下C3DReader来一帧一帧之读取。

 1 using (FileStream fs = new FileStream("文件路径", FileMode.Open, FileAccess.Read))
 2 {
 3     C3DReader reader = new C3DReader(fs);
 4     C3DHeader header = reader.ReadHeader();
 5     C3DParameterDictionary dictionary = reader.ReadParameters();
 6     Int32 index = header.FirstFrameIndex;
 7 
 8     while (true)
 9     {
10         C3DFrame frame = reader.ReadNextFrame(dictionary);
11 
12         if (frame == null)
13         {
14             break;
15         }
16 
17         for (Int16 j = 0; j < frame.Point3Ds.Length; j++)
18         {
19             Console.WriteLine("Frame {0} : X = {1}, Y = {2}, Z = {3}",
20                 index++,
21                 frame.Point3Ds[j].X,
22                 frame.Point3Ds[j].Y,
23                 frame.Point3Ds[j].Z);
24         }
25     }
26 }

对开创一个C3D文件,只需要采用C3DFile.Create()就足以创造一个拖欠的C3D文件之,不含有其他的参数集合。而保存C3D文件则一直以file.SaveTo(“文件路径”)就得了。

于增长参数集合好采取以下的代码:

1 //首先需要添加参数集合,ID为正数
2 file.Parameters.AddGroup(1, "POINT", "");
3 //然后往指定ID的参数集合中添加参数即可
4 file.Parameters[1].Add("USED", "").SetData<Int16>(5);

补给加帧可以应用如下的代码:

1 file.AllFrames.Add(new C3DFrame(new C3DPoint3DData[] {
2     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
3     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
4     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
5     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask},
6     new C3DPoint3DData() { X = x, Y = y, Z = z, Residual = residual, CameraMask = cameraMask} }));

本来,也得以C3DPoint3DData数组换成C3DAnalogSamples数组,或者两者又丰富也可以。

 

【相关链接】

  1. C3D.ORG:http://www.c3d.org/
  2. c3d4sharp – C3D File reading/writing tools written in
    C#:http://code.google.com/p/c3d4sharp/
  3. C3D.NET:https://c3d.codeplex.com/