`
dxx16dxx
  • 浏览: 13578 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

字节流与结构、类之间的转换

 
阅读更多

字节流与结构、类之间的转换
2011年04月26日
  1.  StructLayoutAttribute类
  可将该属性应用于类或结构。
  通常,公共语言运行库控制类或结构的数据字段在托管内存中的物理布局。如果类或结构需要按某种方式排列,则可以使用 StructLayoutAttribute。如果要将类传递给需要指定布局的非托管代码,则显式控制类布局是重要的。
  参数说明
  LayoutKind
  LayoutKind  值之一,它指定类或结构的排列方式。
  Sequential 对象的成员按照它们在被导出到非托管内存时出现的顺序依次布局。这些成员根据在StructLayoutAttribute.Pack中指定的封装进行布局,并且可以是不连续的。
  Explicit 对象的各个成员在非托管内存中的精确位置被显式控制。每个成员必须使用 FieldOffsetAttribute 指示该字段在类型中的位置。
  Auto 运行库自动为非托管内存中的对象的成员选择适当的布局。使用此枚举成员定义的对象不能在托管代码的外部公开。尝试这样做将引发异常。
  CharSet
  指示在默认情况下应如何将类中的字符串数据字段作为 LPWSTR 或 LPSTR 进行封送处理。
  如果 CharSet 字段设置为 CharSet.Unicode,则所有字符串参数在传递到非托管实现之前都转换成 Unicode 字符(LPWSTR)。如果该字段设置为 CharSet.Ansi,则字符串将转换成 ANSI 字符串 (LPSTR)。如果 CharSet 字段设置为CharSet.Auto,则转换与平台相关(在 Windows NT、Windows 2000、Windows XP 和 Windows Server 2003 系列上为 Unicode;在 Windows 98 和 Windows Me 上为 ANSI)。
  Pack
  控制类或结构的数据字段在内存中的对齐方式。
  此字段指示在指定 LayoutKind.Sequential 值时应使用的封装大小。Pack 的值必须为 0、1、2、4、8、16、32、64 或 128。值为 0 则指示封装对齐方式设置为当前平台的默认值。
  Size
  指示类或结构的绝对大小。
  必须大于或等于所有成员的总和。此字段主要由编译器编写器使用,以指定类或结构的总大小(以字节为单位),而且在扩展由结构占用的内存(用于直接的非托管访问)时,此字段也很有用。例如,在使用未直接在元数据中表示的联合时可使用此字段。
  示例
  view plaincopy to clipboardprint?
  [StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]  
  public class MySystemTime   
  {  
  [FieldOffset(0)]public ushort wYear;   
  [FieldOffset(2)]public ushort wMonth;  
  [FieldOffset(4)]public ushort wDayOfWeek;   
  [FieldOffset(6)]public ushort wDay;   
  [FieldOffset(8)]public ushort wHour;   
  [FieldOffset(10)]public ushort wMinute;   
  [FieldOffset(12)]public ushort wSecond;   
  [FieldOffset(14)]public ushort wMilliseconds;   
  } 
  [StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
  public class MySystemTime
  {
  [FieldOffset(0)]public ushort wYear;
  [FieldOffset(2)]public ushort wMonth;
  [FieldOffset(4)]public ushort wDayOfWeek;
  [FieldOffset(6)]public ushort wDay;
  [FieldOffset(8)]public ushort wHour;
  [FieldOffset(10)]public ushort wMinute;
  [FieldOffset(12)]public ushort wSecond;
  [FieldOffset(14)]public ushort wMilliseconds;
  }
  2. MarshalAsAttribute类
  可将该属性应用于参数、字段或返回值。
  该属性为可选属性,因为每个数据类型都有默认的封送处理行为。仅在可以将给定类型封送到多个类型时需要此属性。例如,可将字符串作为 LPStr、LPWStr、LPTStr 或 BStr 封送到非托管代码。默认情况下,公共语言运行时将字符串参数作为 BStr 封送到 COM 方法。可将 MarshalAsAttribute 属性应用于个别的字段或参数,使该字符串作为 LPStr 而不是 BStr 进行封送。类型库导出程序 (Tlbexp.exe) 将封送处理首选项传递给公共语言运行时。
  当用于 COM 互操作或平台调用时,某些参数和返回值将具有不同的默认封送处理行为。默认情况下,运行时将字符串参数(以及值类型中的字段)作为 LPStr 封送到平台调用方法或函数。有关更多信息,请参见 默认封送处理行为。
  参数说明
  UnmanagedType
  数据将被作为该值封送。
  在 System.Runtime.InteropServices.MarshalAsAttribute 中使用 UnmanagedType 枚举,以指定在与非托管代码进行交互的过程中如何对类型进行封送处理。可以使用此枚举对使用简单值类型(I1、I2、I4、I8、R4、R8、U2、U4 和 U8)、.NET Framework 中未提供的非托管类型以及各种杂项类型的代码进行封送处理。
  SizeConst
  指示固定长度数组中的元素数,或要导入的字符串中的字符(不是字节)数。
  对于 System.Runtime.InteropServices.UnmanagedType 枚举的 ByValArray 和 ByValTStr 成员,此字段是必需的。因为元数据的压缩格式限制为 0x1FFFFFFF,所以 SizeConst 的允许值的范围为(>= 0 且 运行库验证不安全代码。
  示例
  view plaincopy to clipboardprint?
  // cs_unsafe_keyword.cs  
  // compile with: /unsafe  
  using System;  
  class UnsafeTest  
  {  
  // Unsafe method: takes pointer to int:  
  unsafe static void SquarePtrParam(int* p)  
  {  
  *p *= *p;  
  }  
  unsafe static void Main()  
  {  
  int i = 5;  
  // Unsafe method: uses address-of operator (&):  
  SquarePtrParam(&i);  
  Console.WriteLine(i);  
  }  
  } 
  // cs_unsafe_keyword.cs
  // compile with: /unsafe
  using System;
  class UnsafeTest
  {
  // Unsafe method: takes pointer to int:
  unsafe static void SquarePtrParam(int* p)
  {
  *p *= *p;
  }
  unsafe static void Main()
  {
  int i = 5;
  // Unsafe method: uses address-of operator (&):
  SquarePtrParam(&i);
  Console.WriteLine(i);
  }
  }
  5. fixed
  fixed 语句禁止垃圾回收器重定位可移动的变量。fixed 语句只能出现在不安全的上下文中。
  fixed 语句设置指向托管变量的指针,并在执行该语句期间“固定”此变量。如果没有 fixed 语句,则指向可移动托管变量的指针的作用很小,因为垃圾回收可能不可预知地重定位变量。C# 编译器只允许在 fixed 语句中分配指向托管变量的指针。
  view plaincopy to clipboardprint?
  unsafe static void TestMethod()  
  {  
  // assume class Point { public int x, y; }  
  // pt is a managed variable, subject to garbage collection.  
  Point pt = new Point();  
  // Using fixed allows the address of pt members to be  
  // taken, and "pins" pt so it isn't relocated.  
  fixed (int* p = &pt.x)  
  {  
  *p = 1;  
  }          
  } 
  unsafe static void TestMethod()
  {
  // assume class Point { public int x, y; }
  // pt is a managed variable, subject to garbage collection.
  Point pt = new Point();
  // Using fixed allows the address of pt members to be
  // taken, and "pins" pt so it isn't relocated.
  fixed (int* p = &pt.x)
  {
  *p = 1;
  }       
  }
  可以用数组或字符串的地址初始化指针:
  view plaincopy to clipboardprint?
  unsafe void Test2()  
  {  
  Point point = new Point();  
  double[] arr = { 0, 1.5, 2.3, 3.4, 4.0, 5.9 };  
  string str = "Hello World";  
  fixed (double* p = arr) { /*...*/ }   // equivalent to p = &arr[0]  
  fixed (char* p = str) { /*...*/ }  // equivalent to p = &str[0]  
  fixed (int* p1 = &point.x)  
  {  
  fixed (double* p2 = &arr[5])  
  {  
  // Do something with p1 and p2.  
  }  
  }  
  } 
  unsafe void Test2()
  {
  Point point = new Point();
  double[] arr = { 0, 1.5, 2.3, 3.4, 4.0, 5.9 };
  string str = "Hello World";
  fixed (double* p = arr) { /*...*/ }   // equivalent to p = &arr[0]
  fixed (char* p = str) { /*...*/ }  // equivalent to p = &str[0]
  fixed (int* p1 = &point.x)
  {
  fixed (double* p2 = &arr[5])
  {
  // Do something with p1 and p2.
  }
  }
  }
  只要指针的类型相同,就可以初始化多个指针:
  view plaincopy to clipboardprint?
  fixed (byte* ps = srcarray, pd = dstarray) {...} 
  fixed (byte* ps = srcarray, pd = dstarray) {...}
  示例
  view plaincopy to clipboardprint?
  class Point  
  {   
  public int x, y;   
  }  
  class FixedTest2   
  {  
  // Unsafe method: takes a pointer to an int.  
  unsafe static void SquarePtrParam (int* p)   
  {  
  *p *= *p;  
  }  
  unsafe static void Main()   
  {  
  Point pt = new Point();  
  pt.x = 5;  
  pt.y = 6;  
  // Pin pt in place:  
  fixed (int* p = &pt.x)   
  {  
  SquarePtrParam (p);  
  }  
  // pt now unpinned  
  Console.WriteLine ("{0} {1}", pt.x, pt.y);  
  }  
  }  
  /* 
  Output: 
  25 6 
  */ 
  class Point
  {
  public int x, y;
  }
  class FixedTest2
  {
  // Unsafe method: takes a pointer to an int.
  unsafe static void SquarePtrParam (int* p)
  {
  *p *= *p;
  }
  unsafe static void Main()
  {
  Point pt = new Point();
  pt.x = 5;
  pt.y = 6;
  // Pin pt in place:
  fixed (int* p = &pt.x)
  {
  SquarePtrParam (p);
  }
  // pt now unpinned
  Console.WriteLine ("{0} {1}", pt.x, pt.y);
  }
  }
  /*
  Output:
  25 6
  */
  Fixed 还可用于创建固定大小的缓冲区。
  view plaincopy to clipboardprint?
  public struct MyArray // This code must appear in an unsafe block  
  {  
  public fixed char pathName[128];  
  } 
  public struct MyArray // This code must appear in an unsafe block
  {
  public fixed char pathName[128];
  }
  在此结构中,pathName 数组具有固定的大小和位置,因此可用在其他不安全的代码中。
  128 个元素的 char 数组的大小为 256 字节。在固定大小的 char 缓冲区中,每个字符始终占用两个字节,而与编码无关。即使将 char 缓冲区封送到具有 CharSet = CharSet.Auto 或 CharSet = CharSet.Ansi 的 API 方法或结构,也是如此。有关更多信息,请参见 CharSet。
  另一种常见的固定大小的数组是 bool 数组。bool 数组中的元素的大小始终为一个字节。bool 数组不适合用于创建位数组或位缓冲区。
  说明: 
  除了用 stackalloc 创建的内存之外,C# 编译器和公共语言运行时 (CLR) 不执行任何安全缓冲区溢出检查。与所有不安全代码一样,请谨慎使用。
  不安全缓冲区与常规数组在以下方面不同:
  不安全缓冲区只能用在不安全上下文中。
  不安全缓冲区始终是向量(或一维数组)。
  数组的声明应包括计数,如 char id[8]。而不能使用 char id[]。
  不安全缓冲区只能是不安全上下文中的结构的实例字段。
  6. stackalloc
  stackalloc 关键字用于不安全的代码上下文中,以便在堆栈上分配内存块。
  view plaincopy to clipboardprint?
  int* fib = stackalloc int[100]; 
  int* fib = stackalloc int[100];
  下面的示例输出斐波那契数列的 100 个数字,其中每个数字都是前两个数字之和。在代码中,大小足够容纳 100 个 int 类型元素的内存块是在堆栈上分配的,而不是在堆上分配的。该块的地址存储在 fib 指针中。此内存不受垃圾回收的制约,因此不必将其钉住(通过使用 fixed)。内存块的生存期受限于定义它的方法的生存期。不能在方法返回之前释放内存。
  stackalloc 仅在局部变量的初始值设定项中有效。
  由于涉及指针类型,因此 stackalloc 要求不安全上下文。有关更多信息,请参见 不安全代码和指针(C# 编程指南)。
  stackalloc 类似于 C 运行时中的 _alloca。
  不安全代码的安全性低于安全替代代码。但是,通过使用 stackalloc 可以自动启用公共语言运行时 (CLR) 中的缓冲区溢出检测功能。如果检测到缓冲区溢出,进程将尽快终止,以最大限度地减小执行恶意代码的机会。
  view plaincopy to clipboardprint?
  class Test  
  {  
  static unsafe void Main()  
  {  
  const int arraySize = 20;  
  int* fib = stackalloc int[arraySize];  
  int* p = fib;  
  *p++ = 1;  
  for (int i = 2; i < arraySize; ++i, ++p)  
  {  
  // Sum the previous two numbers.  
  *p = p[-1] + p[-2];  
  }  
  for (int i = 0; i < arraySize - 1; ++i)  
  {  
  Console.WriteLine(fib);  
  }  
  // Keep the console window open in debug mode.  
  System.Console.WriteLine("Press any key to exit.");  
  System.Console.ReadKey();  
  }  
  }  
  /* 
  Output 
  1 
  1 
  2 
  3 
  5 
  8 
  13 
  21 
  34 
  55 
  89 
  144 
  233 
  377 
  610 
  987 
  1597 
  2584 
  4181 
  */ 
  class Test
  {
  static unsafe void Main()
  {
  const int arraySize = 20;
  int* fib = stackalloc int[arraySize];
  int* p = fib;
  *p++ = 1;
  for (int i = 2; i < arraySize; ++i, ++p)
  {
  // Sum the previous two numbers.
  *p = p[-1] + p[-2];
  }
  for (int i = 0; i < arraySize - 1; ++i)
  {
  Console.WriteLine(fib);
  }
  // Keep the console window open in debug mode.
  System.Console.WriteLine("Press any key to exit.");
  System.Console.ReadKey();
  }
  }
  /*
  Output
  1
  1
  2
  3
  5
  8
  13
  21
  34
  55
  89
  144
  233
  377
  610
  987
  1597
  2584
  4181
  */
  --------------------------------------------------------------------------------
  字节流与结构、类之间的转换
  实际案例
  view plaincopy to clipboardprint?
  using System;  
  using System.Collections.Generic;  
  using System.IO;  
  using System.Text;  
  using System.Runtime.InteropServices;  
  namespace ConsoleApplication1  
  {  
  [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi,Pack=1)]  
  public struct ConfigInfo  
  {  
  public int i;  
  public short s;  
  public byte c;  
  //public fixed byte str[10];  
  //public string GetStr() {  
  //    fixed (byte* p = str) {  
  //        return Marshal.PtrToStringAnsi((IntPtr)p);  
  //    }  
  //}  
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]  
  public string str;  
  }  
  class Program  
  {  
  static void Main(string[] args)  
  {  
  FileStream f = new FileInfo(@"e:\test.dat").OpenRead();  
  byte[] data = new byte[17];  
  f.Read(data, 0, 17);  
  f.Close();  
  unsafe {  
  fixed (byte* pContext = data) {  
  ConfigInfo info = (ConfigInfo)Marshal.PtrToStructure((IntPtr)pContext, typeof(ConfigInfo));  
  Console.WriteLine(info.str);  
  }  
  }  
  Console.ReadLine();  
  }  
  }  
  } 
分享到:
评论

相关推荐

    Java之IO流学习总结

    “存在及合理”我们看看这些字节流中不太对称的几个类吧! LineNumberInputStream 主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。...

    JAVA_API1.6文档(中文)

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    服务器概要设计说明.docx

    目录 功能概述 2 网络通信层 3 连接生命周期的管理 3 接口 3 异步IO缓冲内存池 3 本地数据与字节流数据的互相转换 4 信令和通信数据结构 5 伪代码定义 5 命令管理 7 数据有效性检测 8 文件传输通道 9 日志 10 功能...

    java初学者必看

    14.3 字节流InputStream、OutputStream 14.3.1 字节输入、输出流 14.3.2 字节文件输入、输出流 14.3.3 字节缓冲输入、输出流 14.3.4 字节数据输入、输出流 14.3.5 字节对象输入、输出流 14.4 字符流Reader、...

    java api最新7.0

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象提供...

    [Java参考文档].JDK_API 1.6

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象激活...

    Java 1.6 API 中文 New

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象激活...

    javaSE代码实例

    15.1.4 内部类与外部类之间的成员互访 324 15.1.5 内部类与外部类的预定义对象引用this 327 15.2 局部内部类 328 15.2.1 局部内部类的定义及创建 328 15.2.2 局部变量与局部内部类 329 15.2.3 静态方法中...

    JavaAPI1.6中文chm文档 part1

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    JDK_1_6 API

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象激活...

    JavaAPI中文chm文档 part2

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    [Java参考文档]

    java.nio.charset 定义用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。 java.nio.charset.spi java.nio.charset 包的服务提供者类。 java.rmi 提供 RMI 包。 java.rmi.activation 为 RMI 对象...

    C#全能速查宝典

    分别介绍了C#语言基础、Windows窗体及常用控件、Windows高级控件、控件公共属性、方法及事件、数据库开发、文件、数据流与注册表、GDI+绘图技术和C#高级编程,共包含562个C#编程中常用的属性、方法、类和各种技术,...

    面向对象技术与UML课件及源代码-by 南邮-陈杨

    11.3字节流的输入输出 11.3.1认识字节流 11.3.2如何读写文件 11.3.3如何读写对象 11.4字符流的输入输出 11.4.1认识字符流 11.4.2如何读写文件 11.4.3如何进行键盘输入 11.5和IO操作相关的其他类 11.5.1用...

    基于Java的XML解析与反射设计模式.doc

    就是由于与java对象的完美转换才更贴近于面 向对象的设计思想,让开发人员更易于使用,更易于过度解析xml与项目业务逻辑之间的 联系。xstream不仅对xml的转换非常友好,而且提供annotation注解,可以在avabean中 ...

    java版三级分销源码-gtirb:二进制分析和转换的中间表示

    它旨在促进执行二进制反汇编、分析、转换和漂亮打印的程序之间的二进制 IR 通信。 GTIRB 以 LLVM-IR 为模型,旨在提供类似的功能,鼓励工具之间的通信和互操作性。 该文件的其余部分描述了 GTIRB 的各个方面: 结构 ...

    计算机二级公共基础知识

    根据数据结构中各数据元素之间前后件关系的复杂程度,一般将数据结构分为两大类型:线性结构与非线性结构。 (1)如果一个非空的数据结构满足下列两个条件: ① 有且只有一个根结点; ② 每一个结点最多有一个前件,...

Global site tag (gtag.js) - Google Analytics